From 4c605ec5cf6f32f37845204faa90a70e50b730d2 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 8 Dec 2024 19:40:54 +0800 Subject: [PATCH] Switch files fully to IDB, GUI fixes --- index.html | 2 +- modgui.js | 18 +- modloader.injector.js | 212 ----------------------- modloader.js | 380 +++++++++++++++++++++++------------------- 4 files changed, 220 insertions(+), 392 deletions(-) delete mode 100644 modloader.injector.js diff --git a/index.html b/index.html index e8cf2f0..d2780fa 100644 --- a/index.html +++ b/index.html @@ -169,7 +169,7 @@ - + diff --git a/modgui.js b/modgui.js index 5ef4d75..3eb1d96 100644 --- a/modgui.js +++ b/modgui.js @@ -24,7 +24,7 @@ globalThis.modapi_guikit = "(" + (() => { - +
@@ -162,15 +162,18 @@ globalThis.modapi_guikit = "(" + (() => { font-size: 1.25rem; box-shadow: 0 -4px 6px rgba(0, 0, 0, 0.1); } + #modapi_gui_modTable { + min-width: 40vw; + } `; - async function fileToDataURI(file) { + async function fileToText(file) { return new Promise((res, rej) => { var fr = new FileReader(); fr.addEventListener("error", (e) => { rej(e); }); fr.addEventListener("load", (e) => { res(fr.result); }); - fr.readAsDataURL(file); + fr.readAsText(file); }); } window.modapi_displayModGui = async function (cb) { @@ -191,7 +194,7 @@ globalThis.modapi_guikit = "(" + (() => { document.querySelector("#modapi_gui_container")._cb = cb; var modsList = await getMods(); - var tbody = document.querySelector("#modapi_gui_container .modTable tbody"); + var tbody = document.querySelector("#modapi_gui_container #modapi_gui_modTable tbody"); tbody.innerHTML = ""; modsList.forEach((modtxt, i) => { if (!modtxt) { return } @@ -245,7 +248,6 @@ globalThis.modapi_guikit = "(" + (() => { var button = document.createElement("button"); button.innerText = "Delete"; button.style.height = "3rem"; - button.style.marginTop = "calc(50% - 1.5rem)"; button.addEventListener("click", async () => { await removeMod(i); window.modapi_displayModGui(); @@ -254,7 +256,7 @@ globalThis.modapi_guikit = "(" + (() => { controls.appendChild(button); tr.appendChild(mod); tr.appendChild(spacer); - tr.appendChild(button); + tr.appendChild(controls); tbody.appendChild(tr); }); var once = false; @@ -279,7 +281,7 @@ globalThis.modapi_guikit = "(" + (() => { if (!mod || mod.length === 0) { return; } - await addMod("web@" + mod); + await addMod(mod); window.modapi_displayModGui(); } window.modapi_uploadmod = async () => { @@ -292,7 +294,7 @@ globalThis.modapi_guikit = "(" + (() => { return; } for (let i = 0; i < f.files.length; i++) { - await addMod("web@" + (await fileToDataURI(f.files[i])).replaceAll(";base64", ";fs=" + f.files[i].name + ";base64")); + await addFileMod(f.files[i].name, (await fileToText(f.files[i]))); } window.modapi_displayModGui(); }); diff --git a/modloader.injector.js b/modloader.injector.js deleted file mode 100644 index 66c62d7..0000000 --- a/modloader.injector.js +++ /dev/null @@ -1,212 +0,0 @@ -globalThis.modapi_modloader = `function promisifyIDBRequest(request) { - return new Promise((resolve, reject) => { - request.onsuccess = () => resolve(request.result); - request.onerror = () => reject(request.error); - }); -} - -async function getDatabase() { - const dbRequest = indexedDB.open("EF_MODS"); - const db = await promisifyIDBRequest(dbRequest); - - if (!db.objectStoreNames.contains("filesystem")) { - db.close(); - const version = db.version + 1; - const upgradeRequest = indexedDB.open("EF_MODS", version); - upgradeRequest.onupgradeneeded = (event) => { - const upgradedDb = event.target.result; - upgradedDb.createObjectStore("filesystem"); - }; - return promisifyIDBRequest(upgradeRequest); - } - - return db; -} - -async function getMods() { - const db = await getDatabase(); - const transaction = db.transaction(["filesystem"], "readonly"); - const objectStore = transaction.objectStore("filesystem"); - const object = await promisifyIDBRequest(objectStore.get("mods.txt")); - return object ? (await object.text()).split("|") : []; -} - -async function saveMods(mods) { - const db = await getDatabase(); - const transaction = db.transaction(["filesystem"], "readwrite"); - const objectStore = transaction.objectStore("filesystem"); - const encoder = new TextEncoder(); - const modsData = encoder.encode(mods.join("|")); - const modsBlob = new Blob([modsData], { type: "text/plain" }); - await promisifyIDBRequest(objectStore.put(modsBlob, "mods.txt")); -} - -async function addMod(mod) { - const mods = await getMods(); - mods.push(mod); - await saveMods(mods); -} - -async function removeMod(index) { - const mods = await getMods(); - if (index >= 0 && index < mods.length) { - mods.splice(index, 1); - await saveMods(mods); - } -} - -async function resetMods() { - await saveMods([]); - console.log("Mods reset"); -} - -window.modLoader = async function modLoader(modsArr = []) { - if (!window.eaglerMLoaderMainRun) { - var searchParams = new URLSearchParams(location.search); - searchParams.getAll("mod").forEach((modToAdd) => { - console.log( - "[EaglerML] Adding mod to loadlist from search params: " + modToAdd - ); - modsArr.push(modToAdd); - }); - searchParams.getAll("plugin").forEach((modToAdd) => { - console.log( - "[EaglerML] Adding mod to loadlist from search params: " + modToAdd - ); - modsArr.push(modToAdd); - }); - if ( - !!eaglercraftXOpts && - !!eaglercraftXOpts.Mods && - Array.isArray(eaglercraftXOpts.Mods) - ) { - eaglercraftXOpts.Mods.forEach((modToAdd) => { - console.log( - "[EaglerML] Adding mod to loadlist from eaglercraftXOpts: " + - modToAdd - ); - modsArr.push(modToAdd); - }); - } - - console.log("[EaglerML] Searching in iDB"); - try { - var idbMods = await getMods(); - modsArr = modsArr.concat(idbMods - .filter(x => { return x && x.length > 0 }) - .flatMap(x => { if (x.startsWith("web@")) { return x.replace("web@", "") } return x }) - ); - } catch (error) { - console.error(error); - } - - window.eaglerMLoaderMainRun = true; - } - if (window.noLoadMods === true) { - modsArr.splice(0, modsArr.length); - } - function checkModsLoaded(totalLoaded, identifier) { - console.log( - "[EaglerML] Checking if mods are finished :: " + - totalLoaded + - "/" + - modsArr.length - ); - if (totalLoaded >= modsArr.length) { - clearInterval(identifier); - window.ModGracePeriod = false; - if ( - window.eaglerMLoaderMainRun && - ModAPI && - ModAPI.events && - ModAPI.events.callEvent - ) { - ModAPI.events.callEvent("load", {}); - } - console.log( - "[EaglerML] Checking if mods are finished :: All mods loaded! Grace period off." - ); - } - } - function methodB(currentMod) { - try { - console.log("[EaglerML] Loading " + currentMod + " via method B."); - var script = document.createElement("script"); - script.setAttribute("data-hash", ModAPI.util.hashCode("web@"+currentMod)); - script.src = currentMod; - script.setAttribute("data-isMod", "true"); - script.onerror = () => { - console.log( - "[EaglerML] Failed to load " + currentMod + " via method B!" - ); - script.remove(); - totalLoaded++; - }; - script.onload = () => { - console.log( - "[EaglerML] Successfully loaded " + currentMod + " via method B." - ); - totalLoaded++; - }; - document.body.appendChild(script); - } catch (error) { - console.log( - "[EaglerML] Oh no! The mod " + currentMod + " failed to load!" - ); - totalLoaded++; - } - } - window.ModGracePeriod = true; - var totalLoaded = 0; - var loaderCheckInterval = null; - modsArr.forEach((c) => { - let currentMod = c; - console.log("[EaglerML] Starting " + currentMod); - try { - var req = new XMLHttpRequest(); - req.open("GET", currentMod); - req.onload = function xhrLoadHandler() { - console.log("[EaglerML] Loading " + currentMod + " via method A."); - var script = document.createElement("script"); - script.setAttribute("data-hash", ModAPI.util.hashCode("web@"+currentMod)); - try { - script.src = - "data:text/javascript," + encodeURIComponent(req.responseText); - } catch (error) { - methodB(currentMod); - return; - } - script.setAttribute("data-isMod", "true"); - script.onerror = () => { - console.log( - "[EaglerML] Failed to load " + currentMod + " via method A!" - ); - script.remove(); - totalLoaded++; - }; - script.onload = () => { - console.log( - "[EaglerML] Successfully loaded " + currentMod + " via method A." - ); - totalLoaded++; - }; - document.body.appendChild(script); - }; - req.onerror = function xhrErrorHandler() { - methodB(currentMod); - }; - req.send(); - } catch (error) { - methodB(currentMod); - } - }); - loaderCheckInterval = setInterval(() => { - checkModsLoaded(totalLoaded, loaderCheckInterval); - }, 500); - console.log( - "[EaglerML] Starting to load " + modsArr.length + " mods..." - ); - window.returnTotalLoadedMods = function returnTotalLoadedMods() { - return totalLoaded; - }; -};`; \ No newline at end of file diff --git a/modloader.js b/modloader.js index 02cda35..10b9873 100644 --- a/modloader.js +++ b/modloader.js @@ -1,177 +1,219 @@ -function promisifyIDBRequest(request) { - return new Promise((resolve, reject) => { - request.onsuccess = () => resolve(request.result); - request.onerror = () => reject(request.error); - }); -} - -async function getDatabase() { - const dbRequest = indexedDB.open("EF_MODS"); - const db = await promisifyIDBRequest(dbRequest); - - if (!db.objectStoreNames.contains("filesystem")) { - db.close(); - const version = db.version + 1; - const upgradeRequest = indexedDB.open("EF_MODS", version); - upgradeRequest.onupgradeneeded = (event) => { - const upgradedDb = event.target.result; - upgradedDb.createObjectStore("filesystem"); - }; - return promisifyIDBRequest(upgradeRequest); +globalThis.modapi_modloader = "(" + (() => { + globalThis.promisifyIDBRequest = function promisifyIDBRequest(request) { + return new Promise((resolve, reject) => { + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); } - return db; -} + globalThis.getDatabase = async function getDatabase() { + const dbRequest = indexedDB.open("EF_MODS"); + const db = await promisifyIDBRequest(dbRequest); -async function getMods() { - const db = await getDatabase(); - const transaction = db.transaction(["filesystem"], "readonly"); - const objectStore = transaction.objectStore("filesystem"); - const object = await promisifyIDBRequest(objectStore.get("mods.txt")); - return object ? (await object.text()).split("|") : []; -} + if (!db.objectStoreNames.contains("filesystem")) { + db.close(); + const version = db.version + 1; + const upgradeRequest = indexedDB.open("EF_MODS", version); + upgradeRequest.onupgradeneeded = (event) => { + const upgradedDb = event.target.result; + upgradedDb.createObjectStore("filesystem"); + }; + return promisifyIDBRequest(upgradeRequest); + } -async function saveMods(mods) { - const db = await getDatabase(); - const transaction = db.transaction(["filesystem"], "readwrite"); - const objectStore = transaction.objectStore("filesystem"); - const encoder = new TextEncoder(); - const modsData = encoder.encode(mods.join("|")); - const modsBlob = new Blob([modsData], { type: "text/plain" }); - await promisifyIDBRequest(objectStore.put(modsBlob, "mods.txt")); -} + return db; + } -async function addMod(mod) { - const mods = await getMods(); - mods.push(mod); - await saveMods(mods); -} + globalThis.getMods = async function getMods() { + const db = await getDatabase(); + const transaction = db.transaction(["filesystem"], "readonly"); + const objectStore = transaction.objectStore("filesystem"); + const object = await promisifyIDBRequest(objectStore.get("mods.txt")); + var out = object ? (await object.text()).split("|") : []; + db.close(); + return out; + } -async function removeMod(index) { - const mods = await getMods(); - if (index >= 0 && index < mods.length) { - mods.splice(index, 1); + globalThis.getMod = async function getMod(mod) { + const db = await getDatabase(); + const transaction = db.transaction(["filesystem"], "readonly"); + const objectStore = transaction.objectStore("filesystem"); + const object = await promisifyIDBRequest(objectStore.get("mods/" + mod)); + var out = object ? (await object.text()) : ""; + db.close(); + return out; + } + + globalThis.saveMods = async function saveMods(mods) { + const db = await getDatabase(); + const transaction = db.transaction(["filesystem"], "readwrite"); + const objectStore = transaction.objectStore("filesystem"); + const encoder = new TextEncoder(); + const modsData = encoder.encode(mods.join("|")); + const modsBlob = new Blob([modsData], { type: "text/plain" }); + await promisifyIDBRequest(objectStore.put(modsBlob, "mods.txt")); + db.close(); + } + + globalThis.addMod = async function addMod(mod) { + const mods = await getMods(); + mods.push("web@" + mod); await saveMods(mods); } -} -async function resetMods() { - await saveMods([]); - console.log("Mods reset"); -} + globalThis.addFileMod = async function addFileMod(mod, textContents) { + const mods = await getMods(); + mods.push(mod); + await saveMods(mods); -window.modLoader = async function modLoader(modsArr = []) { - if (!window.eaglerMLoaderMainRun) { - var searchParams = new URLSearchParams(location.search); - searchParams.getAll("mod").forEach((modToAdd) => { - console.log( - "[EaglerML] Adding mod to loadlist from search params: " + modToAdd - ); - modsArr.push(modToAdd); - }); - searchParams.getAll("plugin").forEach((modToAdd) => { - console.log( - "[EaglerML] Adding mod to loadlist from search params: " + modToAdd - ); - modsArr.push(modToAdd); - }); - if ( - !!eaglercraftXOpts && - !!eaglercraftXOpts.Mods && - Array.isArray(eaglercraftXOpts.Mods) - ) { - eaglercraftXOpts.Mods.forEach((modToAdd) => { - console.log( - "[EaglerML] Adding mod to loadlist from eaglercraftXOpts: " + - modToAdd - ); - modsArr.push(modToAdd); - }); - } - - console.log("[EaglerML] Searching in iDB"); - try { - var idbMods = await getMods(); - modsArr = modsArr.concat(idbMods - .filter(x => { return x && x.length > 0 }) - .flatMap(x => { if (x.startsWith("web@")) { return x.replace("web@", "") } return x }) - ); - } catch (error) { - console.error(error); - } - - window.eaglerMLoaderMainRun = true; + const db = await getDatabase(); + const transaction = db.transaction(["filesystem"], "readwrite"); + const objectStore = transaction.objectStore("filesystem"); + const encoder = new TextEncoder(); + const modsData = encoder.encode(textContents); + const modsBlob = new Blob([modsData], { type: "text/plain" }); + await promisifyIDBRequest(objectStore.put(modsBlob, "mods/" + mod)); + db.close(); } - if (window.noLoadMods === true) { - modsArr.splice(0, modsArr.length); - } - function checkModsLoaded(totalLoaded, identifier) { - console.log( - "[EaglerML] Checking if mods are finished :: " + - totalLoaded + - "/" + - modsArr.length - ); - if (totalLoaded >= modsArr.length) { - clearInterval(identifier); - window.ModGracePeriod = false; - if ( - window.eaglerMLoaderMainRun && - ModAPI && - ModAPI.events && - ModAPI.events.callEvent - ) { - ModAPI.events.callEvent("load", {}); + + globalThis.removeMod = async function removeMod(index) { + const mods = await getMods(); + if (index >= 0 && index < mods.length) { + var deleted = mods.splice(index, 1)[0]; + await saveMods(mods); + if (!deleted.startsWith("web@")) { + const db = await getDatabase(); + const transaction = db.transaction(["filesystem"], "readwrite"); + const objectStore = transaction.objectStore("filesystem"); + await promisifyIDBRequest(objectStore.delete("mods/" + deleted)); + db.close(); } - console.log( - "[EaglerML] Checking if mods are finished :: All mods loaded! Grace period off." - ); } } - function methodB(currentMod) { - try { - console.log("[EaglerML] Loading " + currentMod + " via method B."); - var script = document.createElement("script"); - script.setAttribute("data-hash", ModAPI.util.hashCode("web@"+currentMod)); - script.src = currentMod; - script.setAttribute("data-isMod", "true"); - script.onerror = () => { - console.log( - "[EaglerML] Failed to load " + currentMod + " via method B!" - ); - script.remove(); - totalLoaded++; - }; - script.onload = () => { - console.log( - "[EaglerML] Successfully loaded " + currentMod + " via method B." - ); - totalLoaded++; - }; - document.body.appendChild(script); - } catch (error) { - console.log( - "[EaglerML] Oh no! The mod " + currentMod + " failed to load!" - ); - totalLoaded++; - } + + globalThis.resetMods = async function resetMods() { + console.log("Resetting mods..."); + const db = await getDatabase(); + const transaction = db.transaction(["filesystem"], "readwrite"); + const objectStore = transaction.objectStore("filesystem"); + await promisifyIDBRequest(objectStore.clear()); + console.log("Mods reset"); + db.close(); } - window.ModGracePeriod = true; - var totalLoaded = 0; - var loaderCheckInterval = null; - modsArr.forEach((c) => { - let currentMod = c; - console.log("[EaglerML] Starting " + currentMod); - try { - var req = new XMLHttpRequest(); - req.open("GET", currentMod); - req.onload = function xhrLoadHandler() { + + globalThis.modLoader = async function modLoader(modsArr = []) { + if (!window.eaglerMLoaderMainRun) { + var searchParams = new URLSearchParams(location.search); + searchParams.getAll("mod").forEach((modToAdd) => { + console.log( + "[EaglerML] Adding mod to loadlist from search params: " + modToAdd + ); + modsArr.push("web@" + modToAdd); + }); + searchParams.getAll("plugin").forEach((modToAdd) => { + console.log( + "[EaglerML] Adding mod to loadlist from search params: " + modToAdd + ); + modsArr.push("web@" + modToAdd); + }); + if ( + !!eaglercraftXOpts && + !!eaglercraftXOpts.Mods && + Array.isArray(eaglercraftXOpts.Mods) + ) { + eaglercraftXOpts.Mods.forEach((modToAdd) => { + console.log( + "[EaglerML] Adding mod to loadlist from eaglercraftXOpts: " + + modToAdd + ); + modsArr.push(modToAdd); + }); + } + + console.log("[EaglerML] Searching in iDB"); + try { + var idbMods = await getMods(); + modsArr = modsArr.concat(idbMods + .filter(x => { return x && x.length > 0 }) + ); + } catch (error) { + console.error(error); + } + + window.eaglerMLoaderMainRun = true; + } + if (window.noLoadMods === true) { + modsArr.splice(0, modsArr.length); + } + function checkModsLoaded(totalLoaded, identifier) { + console.log( + "[EaglerML] Checking if mods are finished :: " + + totalLoaded + + "/" + + modsArr.length + ); + if (totalLoaded >= modsArr.length) { + clearInterval(identifier); + window.ModGracePeriod = false; + if ( + window.eaglerMLoaderMainRun && + ModAPI && + ModAPI.events && + ModAPI.events.callEvent + ) { + ModAPI.events.callEvent("load", {}); + } + console.log( + "[EaglerML] Checking if mods are finished :: All mods loaded! Grace period off." + ); + } + } + function methodB(currentMod) { + try { + console.log("[EaglerML] Loading " + currentMod + " via method B."); + var script = document.createElement("script"); + script.setAttribute("data-hash", ModAPI.util.hashCode("web@" + currentMod)); + script.src = currentMod; + script.setAttribute("data-isMod", "true"); + script.onerror = () => { + console.log( + "[EaglerML] Failed to load " + currentMod + " via method B!" + ); + script.remove(); + totalLoaded++; + }; + script.onload = () => { + console.log( + "[EaglerML] Successfully loaded " + currentMod + " via method B." + ); + totalLoaded++; + }; + document.body.appendChild(script); + } catch (error) { + console.log( + "[EaglerML] Oh no! The mod " + currentMod + " failed to load!" + ); + totalLoaded++; + } + } + window.ModGracePeriod = true; + var totalLoaded = 0; + var loaderCheckInterval = null; + for (let i = 0; i < modsArr.length; i++) { + let currentMod = modsArr[i]; + var isIDBMod = !currentMod.startsWith("web@"); + if (!isIDBMod) { + currentMod = currentMod.replace("web@", ""); + } + console.log("[EaglerML] Starting " + currentMod); + try { + var responseText = isIDBMod ? await getMod(currentMod) : await (await fetch(currentMod)).text(); console.log("[EaglerML] Loading " + currentMod + " via method A."); var script = document.createElement("script"); - script.setAttribute("data-hash", ModAPI.util.hashCode("web@"+currentMod)); + script.setAttribute("data-hash", ModAPI.util.hashCode((isIDBMod ? "" : "web@") + currentMod)); try { script.src = - "data:text/javascript," + encodeURIComponent(req.responseText); + "data:text/javascript," + encodeURIComponent(responseText); } catch (error) { methodB(currentMod); return; @@ -191,22 +233,18 @@ window.modLoader = async function modLoader(modsArr = []) { totalLoaded++; }; document.body.appendChild(script); - }; - req.onerror = function xhrErrorHandler() { + } catch (error) { methodB(currentMod); - }; - req.send(); - } catch (error) { - methodB(currentMod); + } } - }); - loaderCheckInterval = setInterval(() => { - checkModsLoaded(totalLoaded, loaderCheckInterval); - }, 500); - console.log( - "[EaglerML] Starting to load " + modsArr.length + " mods..." - ); - window.returnTotalLoadedMods = function returnTotalLoadedMods() { - return totalLoaded; + loaderCheckInterval = setInterval(() => { + checkModsLoaded(totalLoaded, loaderCheckInterval); + }, 500); + console.log( + "[EaglerML] Starting to load " + modsArr.length + " mods..." + ); + window.returnTotalLoadedMods = function returnTotalLoadedMods() { + return totalLoaded; + }; }; -}; \ No newline at end of file +}).toString() + ")();" \ No newline at end of file