From 53a0a063ce3a8f5c4dcfcb95114146dd54f6c00d Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Tue, 24 Sep 2024 14:42:30 +0800 Subject: [PATCH 1/6] simple vclip + beggining of asyncsink --- examplemods/AsyncSink.js | 2 ++ examplemods/vclip.js | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 examplemods/AsyncSink.js create mode 100644 examplemods/vclip.js diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js new file mode 100644 index 0000000..80c6449 --- /dev/null +++ b/examplemods/AsyncSink.js @@ -0,0 +1,2 @@ +//AsyncSink is a plugin to debug and override asynchronous methods in EaglercraftX +//WIP \ No newline at end of file diff --git a/examplemods/vclip.js b/examplemods/vclip.js new file mode 100644 index 0000000..8582db3 --- /dev/null +++ b/examplemods/vclip.js @@ -0,0 +1,17 @@ +ModAPI.meta.title("Simple VClip Exploit"); +ModAPI.meta.description("Use .vclip to vertically phase through blocks."); +ModAPI.meta.credits("By ZXMushroom63"); +ModAPI.require("player"); +ModAPI.addEventListener("sendchatmessage", (ev) => { + var msg = ev.message.toLowerCase(); + if (msg.startsWith(".vclip")) { + ev.preventDefault(); + var yOffset = 1; + if (msg.split(" ")[1]) { + yOffset = parseFloat(msg.split(" ")[1]) || 0; + } + ModAPI.player.setPosition(ModAPI.player.posX, ModAPI.player.posY + + yOffset, ModAPI.player.posZ); + ModAPI.displayToChat("[SimpleVClip] VClipped " + yOffset + " blocks."); + } +}); \ No newline at end of file From 96c52f4f100b4399200f401918b4b2f0913e0a3b Mon Sep 17 00:00:00 2001 From: radmanplays <95340057+radmanplays@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:40:19 +0330 Subject: [PATCH 2/6] AdvancedVClip --- examplemods/vclip.js | 80 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/examplemods/vclip.js b/examplemods/vclip.js index 8582db3..28fbbd4 100644 --- a/examplemods/vclip.js +++ b/examplemods/vclip.js @@ -1,17 +1,79 @@ -ModAPI.meta.title("Simple VClip Exploit"); -ModAPI.meta.description("Use .vclip to vertically phase through blocks."); +ModAPI.meta.title("Advanced VClip Exploit"); +ModAPI.meta.description("Use .vclip to vertically phase through blocks with custom packet handling."); ModAPI.meta.credits("By ZXMushroom63"); + +// Custom syntax error function +function syntaxError() { + ModAPI.displayToChat("[AdvancedVClip] Syntax error: Usage is .vclip "); +} + ModAPI.require("player"); ModAPI.addEventListener("sendchatmessage", (ev) => { var msg = ev.message.toLowerCase(); if (msg.startsWith(".vclip")) { ev.preventDefault(); - var yOffset = 1; - if (msg.split(" ")[1]) { - yOffset = parseFloat(msg.split(" ")[1]) || 0; + + var args = msg.split(" "); + if (args.length != 2) { + syntaxError(); + return; } - ModAPI.player.setPosition(ModAPI.player.posX, ModAPI.player.posY - + yOffset, ModAPI.player.posZ); - ModAPI.displayToChat("[SimpleVClip] VClipped " + yOffset + " blocks."); + + var offset = parseFloat(args[1]); + if (isNaN(offset)) { + syntaxError(); + return; + } + + var packetsRequired = Math.ceil(Math.abs(offset / 10)); + if (packetsRequired > 20) { + packetsRequired = 1; // Limit to avoid server kicking for too many packets + } + + var player = ModAPI.player; + var ridingEntity = player.ridingEntity; + + if (ridingEntity != null) { + // Player is riding an entity + for (var packetNumber = 0; packetNumber < (packetsRequired - 1); packetNumber++) { + // Simulate entity movement + ridingEntity.posY += offset / packetsRequired; // Move a fraction of the total offset + player.sendQueue.addToSendQueue({ + "action": "RIDING_JUMP", // Simulate a riding jump action + "entityId": ridingEntity.getEntityId(), + }); + } + + // Final move + ridingEntity.posY += offset / packetsRequired; + player.sendQueue.addToSendQueue({ + "action": "RIDING_JUMP", + "entityId": ridingEntity.getEntityId(), + }); + + } else { + // Player is not riding any entity + for (var packetNumber = 0; packetNumber < (packetsRequired - 1); packetNumber++) { + player.getNetHandler().addToSendQueue({ + "x": player.posX, + "y": player.posY, + "z": player.posZ, + "onGround": true + }); + } + + // Final move + player.getNetHandler().addToSendQueue({ + "x": player.posX, + "y": player.posY + offset, + "z": player.posZ, + "onGround": true + }); + + // Set the player’s final position + player.setPosition(player.posX, player.posY + offset, player.posZ); + } + + ModAPI.displayToChat("[AdvancedVClip] VClipped " + offset + " blocks."); } -}); \ No newline at end of file +}); From a4b93759345fdc3fc51ce2044957f4297627fbd3 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 <116805577+ZXMushroom63@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:38:23 +0800 Subject: [PATCH 3/6] Rename vclip.js to advanced_vclip.js --- examplemods/{vclip.js => advanced_vclip.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examplemods/{vclip.js => advanced_vclip.js} (100%) diff --git a/examplemods/vclip.js b/examplemods/advanced_vclip.js similarity index 100% rename from examplemods/vclip.js rename to examplemods/advanced_vclip.js From a710a55ea255d0a3bb7a40de42df2b98425ae547 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 <116805577+ZXMushroom63@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:52:28 +0800 Subject: [PATCH 4/6] Fix credits on advanced vclip --- examplemods/advanced_vclip.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examplemods/advanced_vclip.js b/examplemods/advanced_vclip.js index 28fbbd4..b6471c9 100644 --- a/examplemods/advanced_vclip.js +++ b/examplemods/advanced_vclip.js @@ -1,6 +1,6 @@ ModAPI.meta.title("Advanced VClip Exploit"); ModAPI.meta.description("Use .vclip to vertically phase through blocks with custom packet handling."); -ModAPI.meta.credits("By ZXMushroom63"); +ModAPI.meta.credits("By radmanplays"); // Custom syntax error function function syntaxError() { From 4dca6aefdd5739363ecac111bbf2991a2861ce66 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Thu, 26 Sep 2024 11:57:14 +0800 Subject: [PATCH 5/6] Fix bugs, and start asyncsink --- examplemods/AsyncSink.js | 132 +++++++++++++++++++++++++++++++++++++- examplemods/npcspawner.js | 12 +++- injector.js | 5 ++ 3 files changed, 146 insertions(+), 3 deletions(-) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index 80c6449..a348701 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -1,2 +1,130 @@ -//AsyncSink is a plugin to debug and override asynchronous methods in EaglercraftX -//WIP \ No newline at end of file +(function AsyncSinkFn() { + //AsyncSink is a plugin to debug and override asynchronous methods in EaglercraftX + function runtimeComponent() { + const booleanResult = (b) => ModAPI.hooks.methods.nlevit_BooleanResult__new(b * 1); + const wrap = ModAPI.hooks.methods.otji_JSWrapper_wrap; + const unwrap = ModAPI.hooks.methods.otji_JSWrapper_unwrap; + function getAsyncHandlerName(name) { + var suffix = `$AsyncHandlers_${name}$_asyncCall_$`; + return ModAPI.hooks._rippedMethodKeys.find(x => x.endsWith(suffix)); + } + var fs_debugging = false; + const encoder = new TextEncoder('utf-8'); + var filesystemPlatform = ModAPI.hooks.methods.nlevit_IndexedDBFilesystem$AsyncHandlers_readWholeFile ? true : false; + if (!filesystemPlatform) { + console.error("AsyncSink requires EaglercraftX u37 or greater to work! Attempting to run anyway..."); + } + const AsyncSink = {}; + const originalSuspend = ModAPI.hooks.TeaVMThread.prototype.suspend; + AsyncSink.startDebugging = function hookIntoSuspend() { + ModAPI.hooks.TeaVMThread.prototype.suspend = function suspend(...args) { + console.log("[AsyncSink] Context suspended! Callback: ", args[0]); + return originalSuspend.apply(this, args); + } + } + AsyncSink.stopDebugging = function unhookFromSuspend() { + ModAPI.hooks.TeaVMThread.prototype.suspend = originalSuspend; + } + + AsyncSink.startDebuggingFS = function hookIntoSuspend() { + fs_debugging = true; + } + AsyncSink.stopDebuggingFS = function unhookFromSuspend() { + fs_debugging = false; + } + + // @type Map + AsyncSink.FS = new Map(); + AsyncSink.FSOverride = new Set(); + AsyncSink.setFile = function setFile(path, data) { + if (typeof data === "string") { + data = encoder.encode(data).buffer; + } + AsyncSink.FSOverride.add(path); + AsyncSink.FS.set(path, data); + return true; + } + + AsyncSink.deleteFile = function deleteFile(path) { + AsyncSink.FSOverride.delete(path); + AsyncSink.FS.delete(path); + return true; + } + + AsyncSink.getFile = function getFile(path) { + return AsyncSink.FS.get(path) || new ArrayBuffer(0); + } + + AsyncSink.fileExists = function fileExists(path) { + return AsyncSink.FS.has(path); + } + + var readWholeFileName = getAsyncHandlerName("readWholeFile"); + var writeWholeFileName = getAsyncHandlerName("writeWholeFile"); + var deleteFileName = getAsyncHandlerName("deleteFile"); + var fileExistsName = getAsyncHandlerName("fileExists"); + + const originalReadWholeFile = ModAPI.hooks.methods[readWholeFileName]; + ModAPI.hooks.methods[readWholeFileName] = function (...args) { + if (fs_debugging) { + console.log("[AsynkSinkFS] File read request sent: " + ModAPI.util.ustr(args[1])); + } + if (AsyncSink.FSOverride.has(ModAPI.util.ustr(args[1]))) { + if (fs_debugging) { + console.log("[AsynkSinkFS] Replied with copy from fake filesystem."); + } + return wrap(AsyncSink.getFile(ModAPI.util.ustr(args[1]))); + } + return originalReadWholeFile.apply(this, args); + }; + + const originalWriteWholeFile = ModAPI.hooks.methods[writeWholeFileName]; + ModAPI.hooks.methods[writeWholeFileName] = function (...args) { + if (fs_debugging) { + console.log("[AsynkSinkFS] File write request sent: " + ModAPI.util.ustr(args[1]), args[2]); + } + if (AsyncSink.FSOverride.has(ModAPI.util.ustr(args[1]))) { + if (fs_debugging) { + console.log("[AsynkSinkFS] Writing to fake filesystem."); + } + AsyncSink.setFile(ModAPI.util.ustr(args[1]), args[2]); + return booleanResult(true); + } + return originalWriteWholeFile.apply(this, args); + }; + + const originalDeleteFile = ModAPI.hooks.methods[deleteFileName]; + ModAPI.hooks.methods[deleteFileName] = function (...args) { + if (fs_debugging) { + console.log("[AsynkSinkFS] File delete request sent: " + ModAPI.util.ustr(args[1])); + } + if (AsyncSink.FSOverride.has(ModAPI.util.ustr(args[1]))) { + if (fs_debugging) { + console.log("[AsynkSinkFS] Deleting entry from fake filesystem."); + } + AsyncSink.deleteFile(ModAPI.util.ustr(args[1])); + return booleanResult(true); + } + return originalDeleteFile.apply(this, args); + }; + + const originalFileExists = ModAPI.hooks.methods[fileExistsName]; + ModAPI.hooks.methods[fileExistsName] = function (...args) { + if (fs_debugging) { + console.log("[AsynkSinkFS] File exists request sent: " + ModAPI.util.ustr(args[1])); + } + if (AsyncSink.FSOverride.has(ModAPI.util.ustr(args[1]))) { + if (fs_debugging) { + console.log("[AsynkSinkFS] Replying with information from fake filesystem."); + } + var result = AsyncSink.fileExists(ModAPI.util.ustr(args[1])); + return booleanResult(result); + } + return originalFileExists.apply(this, args); + }; + globalThis.AsyncSink = AsyncSink; + ModAPI.events.callEvent("lib:asyncsink", {}); + } + runtimeComponent(); + ModAPI.dedicatedServer.appendCode(runtimeComponent); +})(); \ No newline at end of file diff --git a/examplemods/npcspawner.js b/examplemods/npcspawner.js index 1df91bc..a07fbb2 100644 --- a/examplemods/npcspawner.js +++ b/examplemods/npcspawner.js @@ -4,11 +4,18 @@ if (!ModAPI.reflect.getClassById("net.minecraft.entity.player.EntityPlayerMP").instanceOf(event.sender.getRef())) { return; } if (event.command.toLowerCase().startsWith("/spawnnpc")) { + if (!globalThis.AsyncSink) { + return alert("NPC Spawner relies on the AsyncSink library."); + } const world = event.sender.getServerForPlayer(); const senderPos = event.sender.getPosition(); // Create a fake player GameProfile const GameProfileClass = ModAPI.reflect.getClassById("net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile"); + var UUID = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.lax1dude.eaglercraft.v1_8.EaglercraftUUID", "randomUUID")](); + + //Not using UUID to make patching easier for now + const fakeProfile = GameProfileClass.constructors[1](null, ModAPI.util.str("Steve")); // Get the PlayerInteractionManager class @@ -16,8 +23,11 @@ const playerInteractionManager = PlayerInteractionManagerClass.constructors[0](world.getRef()); // Get the EntityPlayerMP class to spawn the fake player + AsyncSink.startDebuggingFS(); const EntityPlayerMPClass = ModAPI.reflect.getClassById("net.minecraft.entity.player.EntityPlayerMP"); - console.log(ModAPI.server.getConfigurationManager()); + var worldNameProp = ModAPI.util.getNearestProperty(ModAPI.server.getRef(), "$worldName"); + var worldName = ModAPI.server.getRef()[worldNameProp]; + console.log(ModAPI.util.ustr(worldName)); const fakePlayer = EntityPlayerMPClass.constructors[0]( ModAPI.server.getRef(), world.getRef(), fakeProfile, playerInteractionManager ); diff --git a/injector.js b/injector.js index 13d01ab..c2418d0 100644 --- a/injector.js +++ b/injector.js @@ -55,6 +55,11 @@ function entriesToStaticVariableProxy(entries, prefix) { return proxy; } async function processClasses(string) { + if (globalThis.doShronk) { + if (!confirm("The minify step is extremely slow, especially on lower-end devices, and can take upwards of 15 minutes.")) { + return; + } + } _status("Beginning patch process..."); await wait(50); var patchedFile = string; From a7578469f037fc38d1881547acc5681b364051b8 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Thu, 26 Sep 2024 12:22:53 +0800 Subject: [PATCH 6/6] patchfixes --- examplemods/AsyncSink.js | 2 ++ postinit.js | 22 +++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index a348701..bb9aab2 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -36,6 +36,7 @@ // @type Map AsyncSink.FS = new Map(); AsyncSink.FSOverride = new Set(); + AsyncSink.MIDDLEWARE = []; AsyncSink.setFile = function setFile(path, data) { if (typeof data === "string") { data = encoder.encode(data).buffer; @@ -75,6 +76,7 @@ } return wrap(AsyncSink.getFile(ModAPI.util.ustr(args[1]))); } + return originalReadWholeFile.apply(this, args); }; diff --git a/postinit.js b/postinit.js index e3ba361..38336d2 100644 --- a/postinit.js +++ b/postinit.js @@ -738,7 +738,13 @@ globalThis.modapi_postinit = "(" + (() => { sender: new Proxy($sender, TeaVM_to_Recursive_BaseData_ProxyConf), command: ModAPI.util.jclStrToJsStr($rawCommand) } - ModAPI.events.callEvent("processcommand", data); + if (data.command.toLowerCase().startsWith("/efdebug")) { + // Utility command to debug the dedicated server. + data.preventDefault = true; + debugger; + } else { + ModAPI.events.callEvent("processcommand", data); + } if (data.preventDefault) { return 0; } @@ -749,10 +755,16 @@ globalThis.modapi_postinit = "(" + (() => { return x; } - ModAPI.items = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.init.Items")].staticVariables, StaticProps_ProxyConf); - ModAPI.blocks = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.init.Blocks")].staticVariables, StaticProps_ProxyConf); - ModAPI.materials = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.block.material.Material")].staticVariables, StaticProps_ProxyConf); - ModAPI.enchantments = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.enchantment.Enchantment")].staticVariables, StaticProps_ProxyConf); + const originalBootstrap = ModAPI.hooks.staticMethods[ModAPI.util.getMethodFromPackage("net.minecraft.init.Bootstrap", "register")]; + ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.init.Bootstrap", "register")] = function (...args) { + var x = originalBootstrap.apply(this, args); + ModAPI.items = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.init.Items")].staticVariables, StaticProps_ProxyConf); + ModAPI.blocks = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.init.Blocks")].staticVariables, StaticProps_ProxyConf); + ModAPI.materials = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.block.material.Material")].staticVariables, StaticProps_ProxyConf); + ModAPI.enchantments = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.enchantment.Enchantment")].staticVariables, StaticProps_ProxyConf); + console.log("[ModAPI] Hooked into bootstrap. .blocks, .items, .materials and .enchantments are now accessible."); + return x; + } const originalOptionsInit = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.gui.GuiOptions", "initGui")]; ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.gui.GuiOptions", "initGui")] = function (...args) {