diff --git a/docs/compiling_client.md b/docs/compiling_client.md new file mode 100644 index 0000000..157d65e --- /dev/null +++ b/docs/compiling_client.md @@ -0,0 +1,53 @@ +# Compiling Eaglercraft with support for EFI +In recent updates of eaglercraft, compiling for EaglerForgeInjector has become a great deal more complicated. To enable reflection and disable obfuscation, follow these steps once you have an EaglercraftX workspace set up: + + +1. In any files named `build.gradle`, set the `obfuscate` property to `false`. +2. In any files named `build.gradle`, find any code that looks like this: + ```javascript + tasks.named("generateJavaScript") { + doLast { + + // NOTE: This step may break at any time, and is not required for 99% of browsers + + def phile = file(folder + "/" + name) + def dest = phile.getText("UTF-8") + def i = dest.substring(0, dest.indexOf("=\$rt_globals.Symbol('jsoClass');")).lastIndexOf("let ") + dest = dest.substring(0, i) + "var" + dest.substring(i + 3) + def j = dest.indexOf("function(\$rt_globals,\$rt_exports){") + dest = dest.substring(0, j + 34) + "\n" + file(folder + "/ES6ShimScript.txt").getText("UTF-8") + "\n" + dest.substring(j + 34) + phile.write(dest, "UTF-8") + } + } + ``` + and delete it. +3. Inside of the `src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/` folder, create a new file called `ForceReflection.java`, with these contents: + ```java + package net.lax1dude.eaglercraft.v1_8.internal.teavm; + + public class ForceReflection { + public static Object myObject; + + public static Object forceInit(Class iClass) { + myObject = new ReflectiveClass(); + try { + myObject = iClass.newInstance(); + } catch (Exception e) { + // TODO: handle exception + } + return myObject; + } + + public static class ReflectiveClass { + } + } + ``` +4. In the same folder, edit `MainClass.java` edit the start of the `main(String[] args)` method to look like this: + ```java + public static void main(String[] args) { + ForceReflection.forceInit(ForceReflection.class); + if(args.length == 1) { + //... rest of method + ``` +5. Finally, build an offline download by using `CompileJS.bat`/`CompileJS.sh` and then `MakeOfflineDownload.bat`/`MakeOfflineDownload.sh`. +6. You can then upload the `EaglercraftX_1.8_Offline_en_US.html` into EaglerForgeInjector. \ No newline at end of file diff --git a/examplemods/Worldedit.js b/examplemods/Worldedit.js index 6d9a637..f3f6c13 100644 --- a/examplemods/Worldedit.js +++ b/examplemods/Worldedit.js @@ -180,6 +180,58 @@ ModAPI.addEventListener("lib:libcustomitems:loaded", () => { } event.preventDefault = true; } + // "this command was made by EymenWSMC. comments included" - radmanplays + if (event.command.toLowerCase().startsWith("//replacenear")) { + const args = event.command.split(" ").slice(1); + if (args.length < 3) { + event.sender.addChatMessage(ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText").constructors[0](ModAPI.util.str(prefix + "Usage: //replacenear "))); + event.preventDefault = true; + return; + } + + const radius = parseInt(args[0]); + const targetBlockName = args[1]; + const replacementBlockName = args[2]; + + const targetBlock = ModAPI.blocks[targetBlockName]; + const replacementBlock = ModAPI.blocks[replacementBlockName]; + if (!targetBlock || !replacementBlock) { + event.sender.addChatMessage(ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText").constructors[0](ModAPI.util.str(prefix + "Invalid block names!"))); + event.preventDefault = true; + return; + } + + + //Replacing logic + const targetBlockState = targetBlock.getDefaultState().getRef(); + const replacementBlockState = replacementBlock.getDefaultState().getRef(); + + const playerPos = event.sender.getPosition(); + const xStart = Math.floor(playerPos.x - radius); + const xEnd = Math.floor(playerPos.x + radius); + const yStart = Math.floor(playerPos.y - radius); + const yEnd = Math.floor(playerPos.y + radius); + const zStart = Math.floor(playerPos.z - radius); + const zEnd = Math.floor(playerPos.z + radius); + + //Replace ity with radoius + for (let x = xStart; x <= xEnd; x++) { + for (let y = yStart; y <= yEnd; y++) { + for (let z = zStart; z <= zEnd; z++) { + const blockPos = blockPosConstructor(x, y, z); + const currentBlock = event.sender.getServerForPlayer().getBlockState(blockPos); + + if (currentBlock.equals(targetBlockState)) { + event.sender.getServerForPlayer().setBlockState(blockPos, replacementBlockState, 3); + } + } + } + } + + //Send messages shit + event.sender.addChatMessage(ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText").constructors[0](ModAPI.util.str(prefix + `Replaced ${targetBlockName} with ${replacementBlockName} within radius ${radius}`))); + event.preventDefault = true; + } }); }); })(); diff --git a/examplemods/block_of_steve_advanced.js b/examplemods/block_of_steve_advanced.js index 24bc3d0..4e860e4 100644 --- a/examplemods/block_of_steve_advanced.js +++ b/examplemods/block_of_steve_advanced.js @@ -44,12 +44,11 @@ function registerSteveClientSide() { ModAPI.util.str("steve") ); blockClass.staticMethods.registerBlock0.method( - 198, //use blockid 198 + 198, //use blockid 198. MAKE SURE TO CHANGE IF YOU ARE MAKING A MOD USING THIS, MAXIMUM BLOCK ID IS 4095. ModAPI.util.str("steve"), block_of_steve ); itemClass.staticMethods.registerItemBlock0.method(block_of_steve); - ModAPI.mc.renderItem.registerBlock(block_of_steve, ModAPI.util.str("steve")); ModAPI.addEventListener("lib:asyncsink", async () => { ModAPI.addEventListener("custom:asyncsink_reloaded", ()=>{ ModAPI.mc.renderItem.registerBlock(block_of_steve, ModAPI.util.str("steve")); @@ -88,6 +87,7 @@ function registerSteveClientSide() { "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE0SURBVDhPpdO9S8NAHMbxy3sVfJmMg6h7FRXXkkUUX0addSjo4OAfIDqLIoiLi3+BRRx0EIQOnV0EcVAIWkR0KIFgrcEktX6vcXD0nuE+5Afhnhw5bWy4qylaidOfVQhT0zFKYozjBHVdzi3TwCZvteaS/0fLD8oGf5OzTeyxNUyE3Ln2HmGctpuxKuS3wd76CgPHsrEj142NeojCkHsFry+4c3aJ6g1OtlZp0Ok4DD4i+Y2GIZ+DMMAhtw+fHu8xi3IDM9t5YfMQF71dLHo+ZjsfXbh4WtnH0vYaqp/BcXGGM3D7BxiYTi+el8uYZWm2gM/VB/Tfaqje4GB5iga2Jv+sUuUa5/ITmOXq7gbnC+MY1r9QvcHG9AgN0lRex1u/ilr7ehqWvBNZvMlRbESfqNhAiG/Pb1bHXpMbFgAAAABJRU5ErkJggg==" )).arrayBuffer()); }); + ModAPI.blocks["steve"] = block_of_steve; } function registerSteveServerSide() { function fixupBlockIds() { @@ -112,16 +112,18 @@ function registerSteveServerSide() { ModAPI.util.str("steve") ); blockClass.staticMethods.registerBlock0.method( - 198, + 198, //use blockid 198. MAKE SURE TO CHANGE IF YOU ARE MAKING A MOD USING THIS, MAXIMUM BLOCK ID IS 4095. ModAPI.util.str("steve"), block_of_steve ); itemClass.staticMethods.registerItemBlock0.method(block_of_steve); fixupBlockIds(); + ModAPI.blocks["steve"] = block_of_steve; }); } ModAPI.dedicatedServer.appendCode(makeSteveBlock); makeSteveBlock(); registerSteveClientSide(); fixupBlockIds(); + ModAPI.dedicatedServer.appendCode(registerSteveServerSide); \ No newline at end of file diff --git a/examplemods/block_of_steve_simple.js b/examplemods/block_of_steve_simple.js index 46b6e25..8814856 100644 --- a/examplemods/block_of_steve_simple.js +++ b/examplemods/block_of_steve_simple.js @@ -26,13 +26,11 @@ function registerSteveClientSide() { ModAPI.util.str("steve") ).$setCreativeTab(creativeBlockTab); blockClass.staticMethods.registerBlock0.method( - 198, //use blockid 198 + 198, //use blockid 198. MAKE SURE TO CHANGE IF YOU ARE MAKING A MOD USING THIS, MAXIMUM BLOCK ID IS 4095. ModAPI.util.str("steve"), block_of_steve ); itemClass.staticMethods.registerItemBlock0.method(block_of_steve); - ModAPI.mc.renderItem.registerBlock(block_of_steve, ModAPI.util.str("steve")); - ModAPI.addEventListener("lib:asyncsink", async () => { ModAPI.addEventListener("custom:asyncsink_reloaded", ()=>{ @@ -72,6 +70,7 @@ function registerSteveClientSide() { "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE0SURBVDhPpdO9S8NAHMbxy3sVfJmMg6h7FRXXkkUUX0addSjo4OAfIDqLIoiLi3+BRRx0EIQOnV0EcVAIWkR0KIFgrcEktX6vcXD0nuE+5Afhnhw5bWy4qylaidOfVQhT0zFKYozjBHVdzi3TwCZvteaS/0fLD8oGf5OzTeyxNUyE3Ln2HmGctpuxKuS3wd76CgPHsrEj142NeojCkHsFry+4c3aJ6g1OtlZp0Ok4DD4i+Y2GIZ+DMMAhtw+fHu8xi3IDM9t5YfMQF71dLHo+ZjsfXbh4WtnH0vYaqp/BcXGGM3D7BxiYTi+el8uYZWm2gM/VB/Tfaqje4GB5iga2Jv+sUuUa5/ITmOXq7gbnC+MY1r9QvcHG9AgN0lRex1u/ilr7ehqWvBNZvMlRbESfqNhAiG/Pb1bHXpMbFgAAAABJRU5ErkJggg==" )).arrayBuffer()); }); + ModAPI.blocks["steve"] = block_of_steve; } function registerSteveServerSide() { function fixupBlockIds() { @@ -98,12 +97,13 @@ function registerSteveServerSide() { ModAPI.util.str("steve") ).$setCreativeTab(creativeBlockTab); blockClass.staticMethods.registerBlock0.method( - 198, + 198, //use blockid 198. MAKE SURE TO CHANGE IF YOU ARE MAKING A MOD USING THIS, MAXIMUM BLOCK ID IS 4095. ModAPI.util.str("steve"), block_of_steve ); itemClass.staticMethods.registerItemBlock0.method(block_of_steve); fixupBlockIds(); + ModAPI.blocks["steve"] = block_of_steve; }); } registerSteveClientSide(); diff --git a/examplemods/diamondBlockCustomCraft.js b/examplemods/diamondBlockCustomCraft.js new file mode 100644 index 0000000..6c3412f --- /dev/null +++ b/examplemods/diamondBlockCustomCraft.js @@ -0,0 +1,51 @@ +(function AddDiamondRecipe() { + ModAPI.meta.title("DiamondCraftingRecipeMod"); + ModAPI.meta.description("Adds a crafting recipe to create diamond blocks from dirt."); + + async function addDiamondRecipe() { + await new Promise((res,rej)=>{var x = setInterval(()=>{if(ModAPI.blocks){clearInterval(x);res();}}, 100);}) + var ObjectClass = ModAPI.reflect.getClassById("java.lang.Object").class; + function ToChar(char) { + return ModAPI.reflect.getClassById("java.lang.Character").staticMethods.valueOf.method(char[0].charCodeAt(0)); + } + + // Define the recipe legend to map characters to items + var recipeLegend = { + "D": { + type: "block", + id: "dirt" // Using dirt blocks + } + }; + + // Define the crafting grid pattern for the recipe + var recipePattern = [ + "DDD", + "DDD", + "DDD" + ]; + + // Convert the recipe pattern and legend into the required format + var recipeInternal = []; + Object.keys(recipeLegend).forEach((key) => { + recipeInternal.push(ToChar(key)); + var ingredient = ModAPI.blocks[recipeLegend[key].id].getRef(); + recipeInternal.push(ingredient); + }); + + var recipeContents = recipePattern.flatMap(row => ModAPI.util.str(row)); + var recipe = ModAPI.util.makeArray(ObjectClass, recipeContents.concat(recipeInternal)); + + // Define the output item as diamond_block + var resultItem = ModAPI.reflect.getClassById("net.minecraft.item.ItemStack").constructors[1](ModAPI.blocks["diamond_block"].getRef(), 1); + + + + // Register the recipe with CraftingManager + var craftingManager = ModAPI.reflect.getClassById("net.minecraft.item.crafting.CraftingManager").staticMethods.getInstance.method(); + ModAPI.hooks.methods.nmic_CraftingManager_addRecipe(craftingManager, resultItem, recipe); + } + + ModAPI.dedicatedServer.appendCode(addDiamondRecipe); + + addDiamondRecipe(); +})(); diff --git a/examplemods/lib.customitems.js b/examplemods/lib.customitems.js index d2b36e3..582f7c3 100644 --- a/examplemods/lib.customitems.js +++ b/examplemods/lib.customitems.js @@ -15,7 +15,7 @@ globalThis.LCI_ITEMDB ||= {}; globalThis.LibCustomItems = { makeItemStack: function makeItemStack(tag) { - return globalThis.LCI_ITEMBD[tag] || null; + return globalThis.LCI_ITEMDB[tag] || null; } }; var useName = ModAPI.util.getMethodFromPackage("net.minecraft.network.NetHandlerPlayServer", "processPlayerBlockPlacement"); @@ -148,7 +148,7 @@ if (globalThis.LCI_RECIPEEVENTS[data.tag]) { globalThis.LCI_RECIPEEVENTS[data.tag](new Proxy(testItem, ModAPI.util.TeaVM_to_Recursive_BaseData_ProxyConf)); } - globalThis.LCI_ITEMBD[data.tag] = new Proxy(testItem, ModAPI.util.TeaVM_to_Recursive_BaseData_ProxyConf); + globalThis.LCI_ITEMDB[data.tag] = new Proxy(testItem, ModAPI.util.TeaVM_to_Recursive_BaseData_ProxyConf); var craftingManager = ModAPI.reflect.getClassById("net.minecraft.item.crafting.CraftingManager").staticMethods.getInstance.method(); if((data.useRecipe !== false) || (data.useRecipe !== "false")) { @@ -162,7 +162,7 @@ LCI_registerItem(data); } LibCustomItems.makeItemStack = function makeItemStack(tag) { - return globalThis.LCI_ITEMBD[tag] || null; + return globalThis.LCI_ITEMDB[tag] || null; } ModAPI.events.callEvent("lib:libcustomitems:loaded", {}); })(); diff --git a/examplemods/no_particles.js b/examplemods/no_particles.js new file mode 100644 index 0000000..cd202de --- /dev/null +++ b/examplemods/no_particles.js @@ -0,0 +1,4 @@ +ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.particle.EffectRenderer", "renderParticles")] = ()=>{}; +ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.particle.EffectRenderer", "hasParticlesInAlphaLayer")] = ()=>{return 0}; +ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.particle.EffectRenderer", "addEffect")] = ()=>{}; +ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.particle.EffectRenderer", "addBlockDestroyEffects")] = ()=>{}; \ No newline at end of file diff --git a/examplemods/unlucky_blocks.js b/examplemods/unlucky_blocks.js index 7943d92..bb7b949 100644 --- a/examplemods/unlucky_blocks.js +++ b/examplemods/unlucky_blocks.js @@ -56,7 +56,7 @@ ModAPI.util.str("unluckiness") ); blockClass.staticMethods.registerBlock0.method( - 544, + 544, //use blockid 544. MAKE SURE TO CHANGE IF YOU ARE MAKING A MOD USING THIS, MAXIMUM BLOCK ID IS 4095. ModAPI.util.str("unluckiness"), block_of_unluckiness ); diff --git a/examplemods/useless_item_example_mod.js b/examplemods/useless_item_example_mod.js new file mode 100644 index 0000000..ae92683 --- /dev/null +++ b/examplemods/useless_item_example_mod.js @@ -0,0 +1,72 @@ +// This is an example mod on how to register an item. +(()=>{ + const itemTexture = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAKZJREFUOE9j/P//PxMDBIBoEP6HREOl4PLIciA2AyPIgMcM//7KgvWSDJjBBpx9/+YvJzc3Sbq12DhB6sEGsJ19/+YnmQawYhigzc7FcPXnN4KugbqAHWQAy9n3b34T4wJkw6EGYLqAoNVQBWS5ANlwZBfAvUCs/0EGkW0AzBKqGoCSDgh5A80F2KMRpAgfAKUT6kcjsfEPUycmKMQgy8AETkgUZWcAS3CPIf4oSPsAAAAASUVORK5CYII="; + //this texture is really baad, so the item appears 2d in game. + ModAPI.meta.title("Adding items demo."); + ModAPI.meta.version("v1.0"); + ModAPI.meta.icon(itemTexture); + ModAPI.meta.description("Requires AsyncSink."); + + function ExampleItem() { + var creativeMiscTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabMisc; + var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item"); + var itemSuper = ModAPI.reflect.getSuper(itemClass, (x) => x.length === 1); + var nmi_ItemExample = function nmi_ItemExample() { + itemSuper(this); //Use super function to get block properties on this class. + this.$setCreativeTab(creativeMiscTab); + } + ModAPI.reflect.prototypeStack(itemClass, nmi_ItemExample); + nmi_ItemExample.prototype.$onItemRightClick = function ($itemstack, $world, $player) { //example of how to override a method + return $itemstack; + } + + function internal_reg() { + var example_item = (new nmi_ItemExample()).$setUnlocalizedName( + ModAPI.util.str("exampleitem") + ); + itemClass.staticMethods.registerItem0.method(432, ModAPI.util.str("exampleitem"), example_item); + ModAPI.items["exampleitem"] = example_item; + + return example_item; + } + + if (ModAPI.items) { + return internal_reg(); + } else { + ModAPI.addEventListener("bootstrap", internal_reg); + } + } + + ModAPI.dedicatedServer.appendCode(ExampleItem); + var example_item = ExampleItem(); + + ModAPI.addEventListener("lib:asyncsink", async () => { + ModAPI.addEventListener("custom:asyncsink_reloaded", ()=>{ + ModAPI.mc.renderItem.registerItem(example_item, ModAPI.util.str("exampleitem")); + }); + AsyncSink.L10N.set("item.exampleitem.name", "Example Item"); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/item/exampleitem.json", JSON.stringify( + { + "parent": "builtin/generated", + "textures": { + "layer0": "items/exampleitem" + }, + "display": { + "thirdperson": { + "rotation": [ -90, 0, 0 ], + "translation": [ 0, 1, -3 ], + "scale": [ 0.55, 0.55, 0.55 ] + }, + "firstperson": { + "rotation": [ 0, -135, 25 ], + "translation": [ 0, 4, 2 ], + "scale": [ 1.7, 1.7, 1.7 ] + } + } + } + )); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/items/exampleitem.png", await (await fetch( + itemTexture + )).arrayBuffer()); + }); +})(); \ No newline at end of file diff --git a/index.html b/index.html index 4eb6424..31d159d 100644 --- a/index.html +++ b/index.html @@ -117,20 +117,7 @@
How do I compile my own unobfuscated unsigned Eaglercraft build? - Once you have a local EaglercraftX workspace setup, in - build.gradle, set the obfuscate property to - false. Then, run CompileJS.bat (or .sh if on - a unix-based os), and then run MakeOfflineDownload.bat. - The outputted offline download will have a much larger file size than - other offline builds. This is the file you should select. (it should - have a naming convention similar to - EaglercraftX_1.8_Offline_en_US.html) - -
- Roadmap? - roadmap. + tutorial here
How does this tool work?