[Global] Major refactors, allow unloading!

This commit is contained in:
Beef 2023-02-04 02:30:46 +00:00
parent 0be8cdac05
commit 0ba9ee600c
10 changed files with 137 additions and 116 deletions

View file

@ -3,10 +3,15 @@ import { findByProps } from "@metro/filters";
import { after } from "@lib/patcher";
const commandsModule = findByProps("getBuiltInCommands")
let commands: ApplicationCommand[] = [];
after("getBuiltInCommands", commandsModule, (args, res) => res.concat(commands));
export function patchCommands() {
const unpatch = after("getBuiltInCommands", commandsModule, (args, res) => res.concat(commands));
return () => {
commands = [];
unpatch();
}
}
export function registerCommand(command: ApplicationCommand): () => void {
// Get built in commands

View file

@ -34,14 +34,16 @@ export function connectToDebugger(url: string) {
});
}
export function patchLogHook() {
after("nativeLoggingHook", globalThis, (args, ret) => {
if (socket?.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({ message: args[0], level: args[1] }));
}
export function patchLogHook() {
const unpatch = after("nativeLoggingHook", globalThis, (args) => {
if (socket?.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ message: args[0], level: args[1] }));
logger.log(args[0]);
});
return () => {
socket && socket.close();
unpatch();
}
}
export const versionHash = "__vendettaVersion";

View file

@ -26,7 +26,7 @@ for (const key in window.modules) {
}
// Function to filter through modules
export const filterModules = (modules: MetroModules, single = false) => (filter: (m: any) => boolean) => {
const filterModules = (modules: MetroModules, single = false) => (filter: (m: any) => boolean) => {
const found = [];
// Get the previous moment locale
@ -53,7 +53,7 @@ export const filterModules = (modules: MetroModules, single = false) => (filter:
found.push(module.default);
}
if(filter(module)) {
if (filter(module)) {
if (single) return module;
else found.push(module);
}

View file

@ -4,6 +4,8 @@ import { awaitSyncWrapper, createStorage, wrapSync } from "@lib/storage";
import logger from "@lib/logger";
import Subpage from "@ui/settings/components/Subpage";
// TODO: Properly implement hash-based updating
type EvaledPlugin = {
onLoad?(): void;
onUnload(): void;
@ -20,7 +22,7 @@ export async function fetchPlugin(id: string, enabled = true) {
let pluginManifest: PluginManifest;
try {
pluginManifest = await (await fetch(new URL("manifest.json", id), { cache: "no-store" })).json();
pluginManifest = await (await fetch(id + "manifest.json", { cache: "no-store" })).json();
} catch {
throw new Error(`Failed to fetch manifest for ${id}`);
}
@ -30,7 +32,7 @@ export async function fetchPlugin(id: string, enabled = true) {
// TODO: Remove duplicate error if possible
try {
// by polymanifest spec, plugins should always specify their main file, but just in case
pluginJs = await (await fetch(new URL(pluginManifest.main || "index.js", id), { cache: "no-store" })).text();
pluginJs = await (await fetch(id + (pluginManifest.main || "index.js"), { cache: "no-store" })).text();
} catch {
throw new Error(`Failed to fetch JS for ${id}`);
}
@ -49,7 +51,6 @@ export async function fetchPlugin(id: string, enabled = true) {
}
export async function evalPlugin(plugin: Plugin) {
// TODO: Refactor to not depend on own window object
const vendettaForPlugins = {
...window.vendetta,
plugin: {
@ -89,7 +90,7 @@ export async function startPlugin(id: string) {
}
}
export function stopPlugin(id: string) {
export function stopPlugin(id: string, disable = true) {
const plugin = plugins[id];
const pluginRet = loadedPlugins[id];
if (!plugin) throw new Error("Attempted to stop non-existent plugin");
@ -102,7 +103,7 @@ export function stopPlugin(id: string) {
}
delete loadedPlugins[id];
plugin.enabled = false;
disable && (plugin.enabled = false);
}
export function removePlugin(id: string) {
@ -111,15 +112,18 @@ export function removePlugin(id: string) {
delete plugins[id];
}
export async function initializePlugins() {
export async function initPlugins() {
await awaitSyncWrapper(plugins);
const allIds = Object.keys(plugins);
await Promise.allSettled(allIds.map((pl) => fetchPlugin(pl, false)));
for (const pl of allIds.filter((pl) => plugins[pl].enabled))
startPlugin(pl);
for (const pl of allIds.filter((pl) => plugins[pl].enabled)) startPlugin(pl);
return stopAllPlugins;
}
const stopAllPlugins = () => Object.keys(plugins).forEach(p => stopPlugin(p, false));
export const getSettings = (id: string) => loadedPlugins[id]?.settings;
export function showSettings(plugin: Plugin) {

54
src/lib/windowObject.ts Normal file
View file

@ -0,0 +1,54 @@
import { VendettaObject } from "@types";
import patcher from "@lib/patcher";
import logger from "@lib/logger";
import settings from "@lib/settings";
import copyText from "@utils/copyText";
import findInReactTree from "@utils/findInReactTree";
import findInTree from "@utils/findInTree";
import * as constants from "@lib/constants";
import * as debug from "@lib/debug";
import * as plugins from "@lib/plugins";
import * as commands from "@lib/commands";
import * as storage from "@lib/storage";
import * as metro from "@metro/filters";
import * as common from "@metro/common";
import * as components from "@ui/components";
import * as toasts from "@ui/toasts";
import * as assets from "@ui/assets";
function without<T extends Record<string, any>>(object: T, ...keys: string[]) {
const cloned = { ...object };
keys.forEach((k) => delete cloned[k]);
return cloned;
}
// I wish Hermes let me do async arrow functions
export default async function windowObject(unloads: any[]): Promise<VendettaObject> {
return {
patcher: without(patcher, "unpatchAll"),
metro: { ...metro, common: { ...common } },
constants: { ...constants },
utils: {
copyText: copyText,
findInReactTree: findInReactTree,
findInTree: findInTree,
},
debug: without(debug, "versionHash", "patchLogHook"),
ui: {
components,
toasts,
assets,
},
plugins: without(plugins, "initPlugins"),
commands: without(commands, "patchCommands"),
storage,
settings,
logger,
version: debug.versionHash,
unload: () => {
unloads.filter(i => typeof i === "function").forEach(p => p());
// @ts-expect-error explode
delete window.vendetta;
}
}
}