diff --git a/examplemods/guns.js b/examplemods/guns.js index 083708b..dfba64e 100644 --- a/examplemods/guns.js +++ b/examplemods/guns.js @@ -63,9 +63,6 @@ } ModAPI.reflect.prototypeStack(itemClass, nmi_ItemPistol); nmi_ItemPistol.prototype.$onItemRightClick = function ($itemstack, $world, $player) { - DamageSourceClass.staticMethods.$callClinit.method(); - //Noticed that the gun only worked after an entity in the world takes damage XD - //TeaVM is very optimised. Using $callClinit tells it to hurry up pretty much lol var cactus = DamageSourceClass.staticVariables.cactus; var world = ModAPI.util.wrap($world); var entityplayer = ModAPI.util.wrap($player); diff --git a/examplemods/guns_craftable.js b/examplemods/guns_craftable.js index 3795cb5..15ba6e1 100644 --- a/examplemods/guns_craftable.js +++ b/examplemods/guns_craftable.js @@ -63,9 +63,6 @@ } ModAPI.reflect.prototypeStack(itemClass, nmi_ItemPistol); nmi_ItemPistol.prototype.$onItemRightClick = function ($itemstack, $world, $player) { - DamageSourceClass.staticMethods.$callClinit.method(); - //Noticed that the gun only worked after an entity in the world takes damage XD - //TeaVM is very optimised. Using $callClinit tells it to hurry up pretty much lol var cactus = DamageSourceClass.staticVariables.cactus; var world = ModAPI.util.wrap($world); var entityplayer = ModAPI.util.wrap($player); diff --git a/examplemods/sliders.js b/examplemods/sliders.js new file mode 100644 index 0000000..e006db3 --- /dev/null +++ b/examplemods/sliders.js @@ -0,0 +1,47 @@ +(function Sliders() { + ModAPI.meta.title("Sliders"); + ModAPI.meta.description("Remove the clamping on sliders."); + ModAPI.meta.credits("By ZXMushroom63"); + + const MathHelper_clamp_float = ModAPI.util.getMethodFromPackage("net.minecraft.util.MathHelper", "clamp_float"); + const GuiOptionSlider_mouseDragged = ModAPI.util.getMethodFromPackage("net.minecraft.client.gui.GuiOptionSlider", "mouseDragged"); + const GuiOptionSlider_mousePressed = ModAPI.util.getMethodFromPackage("net.minecraft.client.gui.GuiOptionSlider", "mousePressed"); + const GameSetting$Options_normalizeValue = ModAPI.util.getMethodFromPackage("net.minecraft.client.settings.GameSettings$Options", "normalizeValue"); + const GameSetting$Options_snapToStepClamp = ModAPI.util.getMethodFromPackage("net.minecraft.client.settings.GameSettings$Options", "snapToStepClamp"); + + const GuiOptionSlider_mouseDragged_fn = ModAPI.hooks.methods[GuiOptionSlider_mouseDragged]; + const GuiOptionSlider_mousePressed_fn = ModAPI.hooks.methods[GuiOptionSlider_mousePressed]; + const GameSetting$Options_normalizeValue_fn = ModAPI.hooks.methods[GameSetting$Options_normalizeValue]; + const GameSetting$Options_snapToStepClamp_fn = ModAPI.hooks.methods[GameSetting$Options_snapToStepClamp]; + const MathHelper_clamp_float_fn = ModAPI.hooks.methods[MathHelper_clamp_float]; + + const fakeClampMethod = (x)=>x; + + ModAPI.hooks.methods[GuiOptionSlider_mouseDragged] = function (...args) { + ModAPI.hooks.methods[MathHelper_clamp_float] = fakeClampMethod; + var ret = GuiOptionSlider_mouseDragged_fn.apply(this, args); + ModAPI.hooks.methods[MathHelper_clamp_float] = MathHelper_clamp_float_fn; + return ret; + } + + ModAPI.hooks.methods[GuiOptionSlider_mousePressed] = function (...args) { + ModAPI.hooks.methods[MathHelper_clamp_float] = fakeClampMethod; + var ret = GuiOptionSlider_mousePressed_fn.apply(this, args); + ModAPI.hooks.methods[MathHelper_clamp_float] = MathHelper_clamp_float_fn; + return ret; + } + + ModAPI.hooks.methods[GameSetting$Options_normalizeValue] = function (...args) { + ModAPI.hooks.methods[MathHelper_clamp_float] = fakeClampMethod; + var ret = GameSetting$Options_normalizeValue_fn.apply(this, args); + ModAPI.hooks.methods[MathHelper_clamp_float] = MathHelper_clamp_float_fn; + return ret; + } + + // ModAPI.hooks.methods[GameSetting$Options_snapToStepClamp] = function (...args) { + // ModAPI.hooks.methods[MathHelper_clamp_float] = fakeClampMethod; + // var ret = GameSetting$Options_snapToStepClamp_fn.apply(this, args); + // ModAPI.hooks.methods[MathHelper_clamp_float] = MathHelper_clamp_float_fn; + // return ret; + // } +})(); \ No newline at end of file diff --git a/examplemods/waypoints.js b/examplemods/waypoints.js new file mode 100644 index 0000000..29f6836 --- /dev/null +++ b/examplemods/waypoints.js @@ -0,0 +1,138 @@ +(function Waypoints() { + ModAPI.meta.title("Waypoints Mod"); + ModAPI.meta.description("Use /setwp to make a waypoint, and /wp to go to it. /remwp to delete a waypoint. /listwp to list all waypoints."); + ModAPI.meta.credits("By blizz828, Block_2222 & ZXMushroom63"); + + ModAPI.dedicatedServer.appendCode(async ()=>{ //The mods should probably be running on the server + function initDB(dbName, storeName) { + return new Promise((resolve, reject) => { + const request = indexedDB.open(dbName, 2); + + request.onupgradeneeded = (event) => { + const db = event.target.result; + if (!db.objectStoreNames.contains(storeName)) { + db.createObjectStore(storeName); + } + resolve(db); + }; + + request.onsuccess = (event) => { + const db = event.target.result; + resolve(db); + }; + + request.onerror = (event) => { + reject('Error opening database: ' + event.target.errorCode); + }; + }); + } + function storeString(dbName, storeName, key, value) { + return initDB(dbName, storeName).then((db) => { + return new Promise((resolve, reject) => { + const transaction = db.transaction(storeName, 'readwrite'); + const store = transaction.objectStore(storeName); + const putRequest = store.put(value, key); + + putRequest.onsuccess = () => { + resolve('String stored successfully.'); + }; + + putRequest.onerror = (event) => { + reject('Error storing string: ' + event.target.errorCode); + }; + }); + }); + } + function retrieveString(dbName, storeName, key) { + return initDB(dbName, storeName).then((db) => { + return new Promise((resolve, reject) => { + const transaction = db.transaction(storeName, 'readonly'); + const store = transaction.objectStore(storeName); + const getRequest = store.get(key); + + getRequest.onsuccess = () => { + if (getRequest.result !== undefined) { + resolve(getRequest.result); + } else { + resolve(''); + } + }; + + getRequest.onerror = (event) => { + resolve(''); + }; + }); + }); + } + + + var data = {}; + try { + data = JSON.parse(await retrieveString("waypoints_db", "waypoints", "waypoints")); + } catch(e) { + //didn't ask + } + + async function saveData() { + await storeString("waypoints_db", "waypoints", "waypoints", JSON.stringify(data)); + } + + + ModAPI.addEventListener("processcommand", (e)=>{ + if (!ModAPI.reflect.getClassById("net.minecraft.entity.player.EntityPlayerMP").instanceOf(e.sender.getRef())) { + return; + } + + if (e.command.toLowerCase().startsWith("/setwp ") && e.sender.canCommandSenderUseCommand(2, ModAPI.util.str("setwp"))) { + e.preventDefault = true; + var pos = e.sender.getPosition(); + var name = ModAPI.util.unstring(e.sender.getName().getRef()); + var waypointId = e.command.split(" ")[1] || "waypoint"; + waypointId = waypointId.replace(/[^a-zA-Z0-9_]/gm, "_"); + if (!data[name]) { + data[name] = {}; + } + data[name][waypointId] = [pos.x,pos.y,pos.z,e.sender.dimension]; + saveData(); + e.sender.addChatMessage(ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText").constructors[0](ModAPI.util.str("Set waypoint "+waypointId+"."))); + } + if (e.command.toLowerCase().startsWith("/wp ") && e.sender.canCommandSenderUseCommand(2, ModAPI.util.str("wp"))) { + e.preventDefault = true; + var name = ModAPI.util.unstring(e.sender.getName().getRef()); + var waypointId = e.command.split(" ")[1]; + if (waypointId && Array.isArray(data?.[name]?.[waypointId])) { + + // Wildly important! regular setPosition triggers minecraft's built in anti-cheat and teleports you back in the same tick. + if (data?.[name]?.[waypointId]?.[3] && (data?.[name]?.[waypointId]?.[3] !== e.sender.dimension)) { + e.sender.travelToDimension(data?.[name]?.[waypointId]?.[3]); + } + + e.sender.setPositionAndUpdate(...data?.[name]?.[waypointId]); + + e.sender.addChatMessage(ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText").constructors[0](ModAPI.util.str("Teleported to waypoint " + waypointId + "."))); + } else { + e.sender.addChatMessage(ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText").constructors[0](ModAPI.util.str("No such waypoint."))); + } + } + if (e.command.toLowerCase().startsWith("/remwp ") && e.sender.canCommandSenderUseCommand(2, ModAPI.util.str("remwp"))) { + e.preventDefault = true; + var name = ModAPI.util.unstring(e.sender.getName().getRef()); + var waypointId = e.command.split(" ")[1] || "waypoint"; + if (!data[name]) { + data[name] = {}; + } + delete data[name][waypointId]; + saveData(); + e.sender.addChatMessage(ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText").constructors[0](ModAPI.util.str("Removed waypoint "+waypointId+"."))); + } + if ((e.command.toLowerCase() === "/listwp") && e.sender.canCommandSenderUseCommand(2, ModAPI.util.str("listwp"))) { + e.preventDefault = true; + var name = ModAPI.util.unstring(e.sender.getName().getRef()); + if (!data[name]) { + data[name] = {}; + } + e.sender.addChatMessage(ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText").constructors[0](ModAPI.util.str("Your waypoints: " + Object.keys(data[name]).join(", ")))); + } + }); + }); +})(); \ No newline at end of file diff --git a/index.html b/index.html index d2780fa..32c56ac 100644 --- a/index.html +++ b/index.html @@ -90,9 +90,8 @@ >Choose .html file...
-     -     - + +          Awaiting input...

diff --git a/injector.js b/injector.js index bb3d8a5..6133bff 100644 --- a/injector.js +++ b/injector.js @@ -1,5 +1,4 @@ globalThis.doEaglerforge = true; -globalThis.optimizePi = true; function wait(ms) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, ms); @@ -8,12 +7,19 @@ function wait(ms) { function _status(x) { document.querySelector("#status").innerText = x; } -function entriesToStaticVariableProxy(entries, prefix) { +function entriesToStaticVariableProxy(entries, prefix, clinitList) { + prefix = prefix.replace( + "var ", + "" + ); if (entries.length === 0) { - return `ModAPI.hooks._rippedStaticProperties[\`${prefix.replace( - "var ", - "" - )}\`]={};`; + return `ModAPI.hooks._rippedStaticProperties[\`${prefix}\`]={};`; + } + if (clinitList.includes(prefix + "_$callClinit")) { + entries.push({ + name: "$callClinit", + variable: prefix + "_$callClinit" + }); } var getComponents = ""; entries.forEach((entry) => { @@ -126,6 +132,10 @@ var main;(function(){` 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); @@ -159,28 +169,6 @@ var main;(function(){` } ); - if(globalThis.optimizePi){ - patchedFile = patchedFile.replaceAll( - "3.1415927410125732 / 180.0", - "0.01745" - ); - - patchedFile = patchedFile.replaceAll( - "180.0 / 3.1415927410125732", - "57.2958" - ); - - patchedFile = patchedFile.replaceAll( - "3.1415927410125732", - "3.14159" - ); - - patchedFile = patchedFile.replaceAll( - "0.01745329238474369", - "0.01745" - ); - } - 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 @@ -189,7 +177,8 @@ var main;(function(){` (match) => { if ( match.includes("__init_") || - match.includes("__clinit_") + match.includes("__clinit_") || + match.includes("_$callClinit") ) { return match; } @@ -236,7 +225,7 @@ var main;(function(){` } }); - var proxy = entriesToStaticVariableProxy(entries, prefix); + var proxy = entriesToStaticVariableProxy(entries, prefix, clinitList); return match + proxy; } @@ -266,7 +255,7 @@ var main;(function(){` } }); - var proxy = entriesToStaticVariableProxy(entries, prefix); + var proxy = entriesToStaticVariableProxy(entries, prefix, clinitList); return proxy + "\n" + match; } diff --git a/modloader.js b/modloader.js index 8f43001..74f1a82 100644 --- a/modloader.js +++ b/modloader.js @@ -63,7 +63,11 @@ globalThis.modapi_modloader = "(" + (() => { globalThis.addFileMod = async function addFileMod(mod, textContents) { const mods = await getMods(); - mods.push(mod); + if (mods.includes(mod)) { + await removeMod(mods.indexOf(mod)); + } else { + mods.push(mod); + } await saveMods(mods); const db = await getDatabase(); diff --git a/postinit.js b/postinit.js index f7b290c..ed53278 100644 --- a/postinit.js +++ b/postinit.js @@ -1,4 +1,4 @@ -globalThis.ModAPIVersion = "v2.3.3"; +globalThis.ModAPIVersion = "v2.3.4"; globalThis.modapi_postinit = "(" + (() => { //EaglerForge post initialization code. //This script cannot contain backticks, escape characters, or backslashes in order to inject into the dedicated server code. @@ -42,7 +42,7 @@ globalThis.modapi_postinit = "(" + (() => { if (!document.currentScript.hasAttribute("data-hash")) { return console.log("[ModAPIMeta] Script does not have a hashcode."); } - ModAPI.meta._titleMap[document.currentScript.getAttribute("data-hash")] = limitSize(title, 16); + ModAPI.meta._titleMap[document.currentScript.getAttribute("data-hash")] = limitSize(title, 36); } ModAPI.meta.icon = function (iconSrc) { if (!document.currentScript || document.currentScript.getAttribute("data-isMod") !== "true") { @@ -60,7 +60,7 @@ globalThis.modapi_postinit = "(" + (() => { if (!document.currentScript.hasAttribute("data-hash")) { return console.log("[ModAPIMeta] Script does not have a hashcode."); } - ModAPI.meta._developerMap[document.currentScript.getAttribute("data-hash")] = limitSize(cd, 36); + ModAPI.meta._developerMap[document.currentScript.getAttribute("data-hash")] = limitSize(cd, 128); } ModAPI.meta.description = function (desc) { if (!document.currentScript || document.currentScript.getAttribute("data-isMod") !== "true") { @@ -78,7 +78,7 @@ globalThis.modapi_postinit = "(" + (() => { if (!document.currentScript.hasAttribute("data-hash")) { return console.log("[ModAPIMeta] Script does not have a hashcode."); } - ModAPI.meta._versionMap[document.currentScript.getAttribute("data-hash")] = limitSize(ver, 6); + ModAPI.meta._versionMap[document.currentScript.getAttribute("data-hash")] = limitSize(ver, 7); } ModAPI.reflect ||= {}; ModAPI.server = ModAPI.serverInstance = null; @@ -207,7 +207,17 @@ globalThis.modapi_postinit = "(" + (() => { } return ModAPI.hooks._teavm.$rt_createDoubleArray(size); } - + + //Proxy to make sure static variables are initialized before access. + function makeClinitProxy(staticVariables, clinit) { + return new Proxy(staticVariables, { + get: function (a, b, c) { + clinit(); + return Reflect.get(a, b, c); + } + }); + } + ModAPI.hooks.regenerateClassMap = function () { ModAPI.hooks._rippedConstructorKeys = Object.keys(ModAPI.hooks._rippedConstructors); ModAPI.hooks._rippedInternalConstructorKeys = Object.keys(ModAPI.hooks._rippedInternalConstructors); @@ -233,7 +243,7 @@ globalThis.modapi_postinit = "(" + (() => { } }); }); - + ModAPI.hooks._rippedConstructorKeys.forEach(constructor => { if (typeof constructor === "string" && constructor.length > 0) { @@ -265,7 +275,11 @@ globalThis.modapi_postinit = "(" + (() => { "class": item || null, "hasMeta": !!item, "instanceOf": function (object) { - return ModAPI.hooks._teavm.$rt_isInstance(object, item || null); + try { + return ModAPI.hooks._teavm.$rt_isInstance(object, item || null); + } catch { + return false; + } }, "compiledName": compiledName } @@ -277,9 +291,6 @@ globalThis.modapi_postinit = "(" + (() => { ModAPI.hooks._classMap[compiledName].superclass = null; ModAPI.hooks._classMap[compiledName].superclassName = null; } - - ModAPI.hooks._classMap[compiledName].staticVariables = ModAPI.hooks._rippedStaticProperties[compiledName]; - ModAPI.hooks._classMap[compiledName].staticVariableNames = Object.keys(ModAPI.hooks._classMap[compiledName].staticVariables || {}); if (item?.["$$constructor$$"]) { //Class does not have any hand written constructors @@ -320,6 +331,10 @@ globalThis.modapi_postinit = "(" + (() => { } } }); + ModAPI.hooks._classMap[compiledName].staticVariables = makeClinitProxy(ModAPI.hooks._rippedStaticProperties[compiledName] || {}, (()=>{ + (ModAPI.hooks._rippedStaticProperties[compiledName].$callClinit ?? (()=>{}))(); + })); + ModAPI.hooks._classMap[compiledName].staticVariableNames = Object.keys(ModAPI.hooks._classMap[compiledName].staticVariables); }); ModAPI.reflect.classes = Object.values(ModAPI.hooks._classMap); console.log("[ModAPI] Regenerated hook classmap."); @@ -336,7 +351,7 @@ globalThis.modapi_postinit = "(" + (() => { //Magical function for making a subclass with a custom constructor that you can easily use super(...) on. ModAPI.reflect.getSuper = function getSuper(reflectClass, filter) { - filter ||= ()=>true; + filter ||= () => true; var initialiser = reflectClass.internalConstructors.find(filter); return function superFunction(thisArg, ...extra_args) { reflectClass.class.call(thisArg); @@ -471,7 +486,7 @@ globalThis.modapi_postinit = "(" + (() => { } ModAPI.events.listeners[name].push(callback); } - console.log("[ModAPI] Added new library listener."); + console.log("[ModAPI] Added new library listener: " + name); return; } if (!callback || typeof callback !== "function") { @@ -482,7 +497,7 @@ globalThis.modapi_postinit = "(" + (() => { ModAPI.events.listeners[name] = []; } ModAPI.events.listeners[name].push(callback); - console.log("[ModAPI] Added new event listener."); + console.log("[ModAPI] Added new event listener: " + name); } else { throw new Error("[ModAPI] This event does not exist!"); } @@ -977,7 +992,7 @@ globalThis.modapi_postinit = "(" + (() => { } return hash; } - + ModAPI.keygen = {}; var registryNamespaceMethod = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.util.RegistryNamespaced", "register")];