diff --git a/backgroundLogger.js b/backgroundLogger.js new file mode 100644 index 0000000..e2398bd --- /dev/null +++ b/backgroundLogger.js @@ -0,0 +1,32 @@ +var backgroundLogs = document.createElement("div"); +backgroundLogs.style = ` + color: lime; + opacity: 0.1; + font-family: monospace; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: -1; + pointer-events: none; + overflow: none; + user-select: none; + `; +const bgLogsList = []; +document.documentElement.appendChild(backgroundLogs); +var dirty = true; +function backgroundLog(text, unSuppress) { + var linesExcess = backgroundLogs.scrollHeight - window.innerHeight; + for (i = 0; i < linesExcess; i++) { + bgLogsList.shift(); + } + bgLogsList.push(text); + dirty = true; + if (!unSuppress) { + return; + } + dirty = false; + backgroundLogs.innerText = bgLogsList.join("\n"); +} +backgroundLog("Awaiting input..."); \ No newline at end of file diff --git a/docs/apidoc/index.md b/docs/apidoc/index.md index 8f1d0dd..d1665ca 100644 --- a/docs/apidoc/index.md +++ b/docs/apidoc/index.md @@ -99,7 +99,10 @@ The ModAPI object has the following methods: - Gets the frames per second of the game - `promisify(asyncJavaMethod: Method | Constructor) : PromisifiedJavaRunner` - Allows running java methods that are @Async/@Async dependent. - - More [PromisifyDocumentation](promisify.md) + - More: [PromisifyDocumentation](promisify.md) + - `addCredit(category: String, contributor: String, contents: String)` + - Lets you easily add credits to Eaglercraft's credits.txt + - eg: `ModAPI.addCredit("My Cool Mod", "Username", " - Coded the mod\n - Wrote somne credits")` ## Handling strings, numbers and booleans to and from java. diff --git a/docs/apidoc/meta.md b/docs/apidoc/meta.md index f226829..346d030 100644 --- a/docs/apidoc/meta.md +++ b/docs/apidoc/meta.md @@ -10,4 +10,8 @@ Methods: - Sets the description of the mod. Character limit of 160. - `ModAPI.meta.icon(iconURL: String)` - Sets the icon of the mod. - - It can be extremely low res, it will not appear blurry. \ No newline at end of file + - It can be extremely low res, it will not appear blurry. +- `ModAPI.meta.version(versionCode: String)` + - Sets the version of the mod. Appended after the title. +- `ModAPI.meta.config(configFn: Function)` + - Once the client is fully loaded, creates a button in the mod manager GUI that runs the specified function when pressed. \ No newline at end of file diff --git a/docs/apidoc/promisify.md b/docs/apidoc/promisify.md index 46dd7ec..195e108 100644 --- a/docs/apidoc/promisify.md +++ b/docs/apidoc/promisify.md @@ -31,7 +31,7 @@ var asyncDownloadRemoteURI = ModAPI.promisify(ModAPI.hooks.methods.nlevi_Platfor console.log(typeof asyncDownloadRemoteURI); //Logs function ``` -When it is called, like any other asyncronoush function, it returns a `Promise` object. +When it is called, like any other asyncronous function, it returns a `Promise` object. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise You can replace the argument with any other method or constructor, including non asynchronous ones. \ No newline at end of file diff --git a/index.html b/index.html index 6032025..7b73985 100644 --- a/index.html +++ b/index.html @@ -90,8 +90,12 @@ >Choose .html file...
- -     + + +     + +     +      Awaiting input...

@@ -163,6 +167,7 @@ `; var freezeCallstack = `if(ModAPI.hooks.freezeCallstack){return false};`; + diff --git a/injector.js b/injector.js index fcf1995..13ce07d 100644 --- a/injector.js +++ b/injector.js @@ -1,4 +1,4 @@ -globalThis.ModAPIVersion = "v2.7"; +globalThis.ModAPIVersion = "v2.7.3"; globalThis.doEaglerforge = true; document.querySelector("title").innerText = `EaglerForge Injector ${ModAPIVersion}`; document.querySelector("h1").innerText = `EaglerForge Injector ${ModAPIVersion}`; @@ -8,6 +8,7 @@ function wait(ms) { }); } function _status(x) { + backgroundLog(x, true); document.querySelector("#status").innerText = x; } function entriesToStaticVariableProxy(entries, prefix, clinitList) { @@ -77,6 +78,7 @@ async function processClasses(string) { if (!confirm("The minify step is extremely slow, especially on lower-end devices, and can take upwards of 15 minutes.")) { return; } + backgroundLog("[MINIFY] Minify warning bypassed."); } _status("Beginning patch process..."); await wait(50); @@ -101,6 +103,7 @@ var main;(function(){` "var main;\n(function() {", modapi_preinit + "var main;\n(function() {" ); + backgroundLog("[JSPATCH] Adding pre-init script"); patchedFile = patchedFile.replace( /function \$rt_metadata\(data\)( ?){/gm, `function $rt_metadata(data) { @@ -108,20 +111,21 @@ var main;(function(){` ModAPI.hooks._rippedData.push(data); /*/EaglerForge Client Patch/*/` ); - + backgroundLog("[JSPATCH] Redirecting $rt_metadata to ModAPI.hooks._rippedData"); patchedFile = patchedFile.replaceAll( `return thread != null && thread.isResuming()`, (match) => { return freezeCallstack + match; } ); - + backgroundLog("[JSPATCH] Freeze-callstack patch on TeaVMThread.isResuming()"); patchedFile = patchedFile.replaceAll( `return thread != null && thread.isSuspending();`, (match) => { return freezeCallstack + match; } ); + backgroundLog("[JSPATCH] Freeze-callstack patch on TeaVMThread.isSuspending()"); patchedFile = patchedFile.replaceAll( `return $rt_currentNativeThread;`, @@ -132,12 +136,12 @@ var main;(function(){` ); } ); + backgroundLog("[JSPATCH] Freeze-callstack patch thread getter"); patchedFile = patchedFile.replaceAll("function TeaVMThread(", "globalThis.ModAPI.hooks.TeaVMThread = TeaVMThread;\nfunction TeaVMThread("); _status("Getting clinit list..."); var clinitList = [...patchedFile.matchAll(/^[\t ]*function \S+?_\S+?_\$callClinit\(/gm)].map(x => x[0].replaceAll("function ", "").replaceAll("(", "").trim()); - console.log(clinitList); _status("Extracting constructors and methods..."); await wait(50); @@ -158,6 +162,8 @@ var main;(function(){` } ); + backgroundLog("-> Extract contructor 1"); + const extractInternalConstructorRegex = /^\s*function (\S*?)__init_\d*?\(\$this/gm; //same as extract constructor regex, but only allow $this as first argument patchedFile = patchedFile.replaceAll( @@ -172,6 +178,8 @@ var main;(function(){` } ); + backgroundLog("-> Extract contructor 2"); + const extractInstanceMethodRegex = /^[\t ]*function \S+?_\S+?_\S+?\((\$this)?/gm; // /^[\t ]*function \S+?_\S+?_\S+?\(\$this/gm const extractInstanceMethodFullNameRegex = /function (\S*?)\(/gm; // /function (\S*?)\(\$this/gm @@ -198,6 +206,10 @@ var main;(function(){` ); } ); + + backgroundLog("-> Extract instance methods"); + backgroundLog("-> Expose instance methods"); + var staticVariables = [ ...patchedFile.matchAll(/var \S+?_\S+?_\S+? = /gm), ].flatMap((x) => { @@ -205,6 +217,7 @@ var main;(function(){` }).filter(x => { return (!x.includes("$_clinit_$")) && (!x.includes("$lambda$")) }); + backgroundLog("-> Extract static variables"); //Also stores classes from $rt_classWithoutFields(0) patchedFile = patchedFile.replaceAll( /var \S+?_\S+? = \$rt_classWithoutFields\(\S*?\);/gm, @@ -240,6 +253,7 @@ var main;(function(){` ); //Edge cases. sigh //Done: add support for static properties on classes with constructors like this: function nmcg_GuiMainMenu() { + backgroundLog("-> Expose static variables"); patchedFile = patchedFile.replaceAll( @@ -307,10 +321,13 @@ var main;(function(){` \`; } - _status("[ASYNC_PLUGIN_1] Parsing html..."); + _status("[MINIFY] Parsing html..."); await wait(50); const parser = new DOMParser(); const doc = parser.parseFromString(inputHtml, 'text/html'); @@ -40,18 +34,20 @@ async function shronk(input) { for (let i = 0; i < scriptTags.length; i++) { const scriptTag = scriptTags[i]; const code = scriptTag.textContent; - _status("[ASYNC_PLUGIN_1] Transpiling script #" + (i + 1) + " of length " + Math.round(code.length / 1000) + "k..."); + _status("[MINIFY] Transpiling script #" + (i + 1) + " of length " + Math.round(code.length / 1000) + "k..."); await wait(150); const output = Babel.transform(code, { - plugins: [] + plugins: globalThis.doShronkPlus ? [ + MINIFY() + ] : [] }); scriptTag.textContent = output.code; await wait(10); } - _status("[ASYNC_PLUGIN_1] Job complete!"); + _status("[MINIFY] Job complete!"); await wait(50); if (isHtml) { diff --git a/modgui.js b/modgui.js index 3eb1d96..6b58b16 100644 --- a/modgui.js +++ b/modgui.js @@ -10,7 +10,9 @@ globalThis.modapi_guikit = "(" + (() => { "hey you should check out https://github.com/ZXMushroom63/scratch-gui", "99% of people stop gambling before they win big.", "Now with free estradiol!", - "Now with H.I.V (Hyper Injected Virtual-debugger)" + "Now with H.I.V (Hyper Injected Virtual-debugger)", + "asdasd", + "Star us on GitHub to support us! https://github.com/EaglerForge/EaglerForgeInjector" ]; var gui = `
@@ -184,7 +186,8 @@ globalThis.modapi_guikit = "(" + (() => { cb ||= document.querySelector("#modapi_gui_container")._cb; document.querySelector("#modapi_gui_container").remove(); } - + + var element = document.createElement("div"); element.innerHTML = gui.replace("{splash_msg}", splashes[Math.floor(Math.random() * splashes.length)]); @@ -245,15 +248,28 @@ globalThis.modapi_guikit = "(" + (() => { spacer.classList.add("nothing"); var controls = document.createElement("td"); - var button = document.createElement("button"); - button.innerText = "Delete"; - button.style.height = "3rem"; - button.addEventListener("click", async () => { + var deleteBtn = document.createElement("button"); + deleteBtn.innerText = "Delete"; + deleteBtn.style.height = "3rem"; + deleteBtn.addEventListener("click", async () => { await removeMod(i); window.modapi_displayModGui(); }); - button.classList.add("button"); - controls.appendChild(button); + deleteBtn.classList.add("button"); + controls.appendChild(deleteBtn); + + if (typeof ModAPI.meta._configMap[hash] === "function") { + var configBtn = document.createElement("button"); + configBtn.innerText = "Config"; + configBtn.style.height = "3rem"; + configBtn.style.marginLeft = "1rem"; + configBtn.addEventListener("click", async () => { + ModAPI.meta._configMap[hash](); + }); + configBtn.classList.add("button"); + controls.appendChild(configBtn); + } + tr.appendChild(mod); tr.appendChild(spacer); tr.appendChild(controls); diff --git a/postinit.js b/postinit.js index 53b8868..f75e2df 100644 --- a/postinit.js +++ b/postinit.js @@ -3,6 +3,7 @@ globalThis.modapi_postinit = "(" + (() => { //This script cannot contain backticks, escape characters, or backslashes in order to inject into the dedicated server code. var startedModLoader = false; var BACKSLASH = String.fromCharCode(92); + var LF = String.fromCharCode(10); var STRIP_COMMENTS = new RegExp(atob("KChcL1wvLiokKXwoXC9cKltcc1xTXSo/XCpcLykp"), "gm"); var ARGUMENT_NAMES = new RegExp(atob("KFteXHMsXSsp"), "g"); @@ -21,17 +22,66 @@ globalThis.modapi_postinit = "(" + (() => { ModAPI.meta = {}; ModAPI.meta._titleMap = {}; ModAPI.meta._descriptionMap = {}; + ModAPI.meta._configMap = {}; ModAPI.meta._developerMap = {}; ModAPI.meta._iconMap = {}; ModAPI.meta._versionMap = {}; + const credits = {}; + ModAPI.addCredit = function (category, name, contents) { + if (!credits[category]) { + credits[category] = []; + } + credits[category].push(LF + LF + " " + name + ": " + LF + LF + contents); + } + function getCreditsString() { + return Object.entries(credits).map((entry) => { + return " "+entry[0] + LF + " " + (new Array(entry[0].length)).fill("~").join("") + entry[1].join("") + LF + LF + LF; + }).join(""); + } ModAPI.array = {}; ModAPI.version = "__modapi_version_code__"; ModAPI.flavour = "injector"; ModAPI.GNU = "terry pratchett"; - ModAPI.credits = ["ZXMushroom63", "radmanplays", "Murturtle", "OtterCodes101", "TheIdiotPlays", "OeildeLynx31", "Stpv22"]; + + ModAPI.addCredit("EaglerForge Devs", "ZXMushroom63", + " - Built the original PluginAPI for EaglerReborn" + LF + + " - Built EaglerForgeInjector as a procedural replacement for EaglerForge clients" + LF + + " - Made the mod loader and gui loader" + LF + + " - Added singleplayer support" + LF + + " - Made the AsyncSink corelib"); + + ModAPI.addCredit("EaglerForge Devs", "radmanplays", + " - Ported and maintained EaglerReborn's PluginAPI to modern version of eaglercrafts (u22+)" + LF + + " - Rebranded PluginAPI to ModAPI" + LF + + " - Added various new features to ModAPI" + LF + + " - Made the worldedit mod + a few other mods"); + + ModAPI.addCredit("EaglerForge Devs", "LeahOnBrainrot / OtterCodes101 / OtterDev", + " - Created EaglerReborn" + LF + + " - EaglerForge developer" + LF + + " - Helped update the client to newer versions" + LF + + " - Made signed clients work" + LF + + " - Maintainer nowadays" + LF + + " - Various bug fixes for EaglerForgeInjector"); + + ModAPI.addCredit("EaglerForge Devs", "Murturtle", + " - Added the render event to EaglerForgeInjector" + LF + + " - Added pi optimiser to the injector (now removed)"); + + ModAPI.addCredit("EaglerForge Devs", "TheIdiotPlays", + " - Made the mod manager GUI"); + + ModAPI.addCredit("EaglerForge Devs", "OeildeLynx31", + " - Work on the worldedit mod"); + + ModAPI.addCredit("EaglerForge Devs", "Stpv22", + " - Made the mod gui open before the client starts"); function limitSize(x, n) { + if (!x) { + return ""; + } if (x.length > n) { return x.substring(0, n) + "…"; } else { @@ -41,20 +91,20 @@ globalThis.modapi_postinit = "(" + (() => { function arraysAreSame(arr1, arr2) { if (!arr1 || !arr2) return false; - if(arr1 === arr2) + if (arr1 === arr2) return true; if (arr1.length !== arr2.length) return false; - for (var i = 0, l=arr1.length; i < l; i++) { + for (var i = 0, l = arr1.length; i < l; i++) { if (arr1[i] instanceof Array && arr2[i] instanceof Array) { if (!arr1[i].equals(arr2[i])) - return false; + return false; } else if (arr1[i] !== arr2[i]) { - return false; - } - } + return false; + } + } return true; } function getParamNames(func) { @@ -115,6 +165,18 @@ globalThis.modapi_postinit = "(" + (() => { } ModAPI.meta._descriptionMap[document.currentScript.getAttribute("data-hash")] = limitSize(desc, 160); } + ModAPI.meta.config = function (conf) { + if (typeof conf !== "function") { + return console.log("[ModAPIMeta] Config value was not a function"); + } + if (!document.currentScript || document.currentScript.getAttribute("data-isMod") !== "true") { + return console.log("[ModAPIMeta] Cannot set meta for non-mod script."); + } + if (!document.currentScript.hasAttribute("data-hash")) { + return console.log("[ModAPIMeta] Script does not have a hashcode."); + } + ModAPI.meta._configMap[document.currentScript.getAttribute("data-hash")] = conf; + } ModAPI.meta.version = function (ver) { if (!document.currentScript || document.currentScript.getAttribute("data-isMod") !== "true") { return console.log("[ModAPIMeta] Cannot set meta for non-mod script."); @@ -343,7 +405,7 @@ globalThis.modapi_postinit = "(" + (() => { "getConstructorByArgs": function (...argNames) { if (!argumentCache) { argumentCache = []; - this.internalConstructors.forEach(x=>{ + this.internalConstructors.forEach(x => { argumentCache.push(getParamNames(x).slice(1).map(y => y.substring(1))); }); } @@ -771,7 +833,7 @@ globalThis.modapi_postinit = "(" + (() => { if (!object) { return null; } - if (prop in object) { + if ((prop in object) && Object.keys(object).includes(prop)) { return prop; } var possibleKeys = Object.keys(object).filter(x => { return x.startsWith(prop) }); @@ -780,7 +842,7 @@ globalThis.modapi_postinit = "(" + (() => { }) return possibleKeys.sort((a, b) => { return a.length - b.length; - })[0] || null; + })[0] || prop; } ModAPI.util.modifyFunction = function (fn, patcherFn) { @@ -821,6 +883,22 @@ globalThis.modapi_postinit = "(" + (() => { return originalUpdate.apply(this, args); }; + const getCreditsName = ModAPI.util.getMethodFromPackage("net.lax1dude.eaglercraft.v1_8.EagRuntime", "getResourceString"); + const originalGetCredits = ModAPI.hooks.methods[getCreditsName]; + ModAPI.hooks.methods[getCreditsName] = function ($path) { + if (!$path) { + return originalGetCredits.apply(this, [$path]); + } + if (ModAPI.util.ustr($path).toLowerCase().endsWith("credits.txt")) { + var out = originalGetCredits.apply(this, [$path]); + out = ModAPI.util.ustr(out); + out = getCreditsString() + out; + out = ModAPI.util.str(out); + return out; + } + return originalGetCredits.apply(this, [$path]); + }; + const initMethodName = ModAPI.util.getMethodFromPackage("net.minecraft.client.Minecraft", "startGame"); const originalInit = ModAPI.hooks.methods[initMethodName]; ModAPI.hooks.methods[initMethodName] = function (...args) { @@ -1105,7 +1183,7 @@ globalThis.modapi_postinit = "(" + (() => { } ModAPI.keygen.entity = function (entity) { var hashMap = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticVariables.idToClassMapping).getCorrective(); - var values = hashMap.keys.getRef().data.filter(x=>hashMap.get(x)); + var values = hashMap.keys.getRef().data.filter(x => hashMap.get(x)); return qhash(entity, values, 127); } }).toString() + ")();";