From 3beaf52ba69b35db9534547853267744cc1b8a15 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Thu, 16 Jan 2025 08:11:46 +0800 Subject: [PATCH 01/41] add afkmod.js --- examplemods/afkmod.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 examplemods/afkmod.js diff --git a/examplemods/afkmod.js b/examplemods/afkmod.js new file mode 100644 index 0000000..d56dc5f --- /dev/null +++ b/examplemods/afkmod.js @@ -0,0 +1,28 @@ +(function AntiAFKMod() { + ModAPI.meta.title("Anti AFK Mod"); + ModAPI.meta.credits("By ZXMushroom63"); + ModAPI.meta.description("Type .afk in game chat to toggle the mod"); + ModAPI.require("player"); + var active = null; + function queueJump() { + try { + ModAPI.player.jump(); + } catch (error) { + + } + active = setTimeout(queueJump, 15000 + (10000 * Math.random())); + } + ModAPI.addEventListener("sendchatmessage", (e)=>{ + if (e.message.toLowerCase() === ".afk") { + if (active === null) { + queueJump(); + ModAPI.displayToChat("Deactivated anti-afk mod"); + } else { + clearTimeout(active); + active = null; + ModAPI.displayToChat("Activated anti-afk mod"); + } + e.preventDefault = true; + } + }); +})(); \ No newline at end of file From ed8712bb88dff4a7dadbb0db24153ab91ad6efa4 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Thu, 16 Jan 2025 08:13:30 +0800 Subject: [PATCH 02/41] fix a few bugs --- examplemods/afkmod.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examplemods/afkmod.js b/examplemods/afkmod.js index d56dc5f..2983495 100644 --- a/examplemods/afkmod.js +++ b/examplemods/afkmod.js @@ -16,11 +16,11 @@ if (e.message.toLowerCase() === ".afk") { if (active === null) { queueJump(); - ModAPI.displayToChat("Deactivated anti-afk mod"); + ModAPI.displayToChat("Activated anti-afk mod!\nI recommend using the sliders mod to decrease your max fps to save battery."); } else { clearTimeout(active); active = null; - ModAPI.displayToChat("Activated anti-afk mod"); + ModAPI.displayToChat("Deactivated anti-afk mod."); } e.preventDefault = true; } From 778003d3c90a8e658b8a3e918047c6200813738b Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Fri, 17 Jan 2025 21:30:27 +0800 Subject: [PATCH 03/41] upgrade readme --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ae2bdd5..d4b7319 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # EaglerForgeInjector -An advanced modding API injector for vanilla eaglercraft builds. +An advanced modding API injector for unminified, unobfuscated, unsigned eaglercraft builds. Current features: - Method hooking/monkey patching - Reflection @@ -13,5 +13,11 @@ Go to https://eaglerforge.github.io/EaglerForgeInjector/ and upload an unminifie #### Portable Offline Download this repository as a .zip, and extract it. Open index.html with your preferred browser (use `ctrl` + `O` on a new tab) and upload an unminified, unobfuscated, unsigned EaglercraftX offline download. +#### How does it work? +This tool matches patterns in eaglercraft builds and adds patching code to let you modify how the code works at runtime. It then adds a [corelib](./postinit.js) that initialises the `ModAPI` object. + +#### History +EaglerForgeInjector is a replacement for the `ModAPI` in the [old eaglerforge](https://github.com/EaglerForge/EaglerForge-old), which was maintained by @radmanplays. The legacy eaglerforge was a port of [OtterDev's EaglerReborn (dmca'd)](https://github.com/EaglerReborn/reborn)'s `PluginAPI` (created by me, @ZXMushroom63) to run on newer versions of Eaglercraft, with a few improvements and new features. Unlike EaglerForgeInjector, both legacy eaglerforge and eaglerreborn manually exposed properties and methods one by one. + ## Discord server [https://discord.gg/rbxN7kby5W](https://discord.gg/rbxN7kby5W) From 7c5b445d44615d161e6f164639ef315f3fc82418 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sat, 18 Jan 2025 22:08:01 +0800 Subject: [PATCH 04/41] fix l18n issue with asyncsink --- examplemods/AsyncSink.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index dd61ac8..fdaa0a9 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -160,6 +160,16 @@ ModAPI.meta.credits("By ZXMushroom63"); return originalL10NRead.apply(this, args); }; + const L18NFormat = ModAPI.util.getMethodFromPackage("net.minecraft.client.resources.I18n", "format"); + const originalL18NFormat = ModAPI.hooks.methods[L18NFormat]; + ModAPI.hooks.methods[L18NFormat] = function (...args) { + var key = ModAPI.util.ustr(args[0]); + if (AsyncSink.L10N.has(key)) { + args[0] = ModAPI.util.str(AsyncSink.L10N.get(key)); + } + return originalL18NFormat.apply(this, args); + }; + const L10NCheck = ModAPI.util.getMethodFromPackage("net.minecraft.util.StatCollector", "canTranslate"); const originalL10NCheck = ModAPI.hooks.methods[L10NCheck]; ModAPI.hooks.methods[L10NCheck] = function (...args) { @@ -203,7 +213,7 @@ ModAPI.meta.credits("By ZXMushroom63"); } else { resourcePackList.resourcePacks.push(pack); } - + const writeableTransaction = db.transaction(["filesystem"], "readwrite"); const writeableObjectStore = writeableTransaction.objectStore("filesystem"); await promisifyIDBRequest(writeableObjectStore.put({ @@ -255,7 +265,7 @@ ModAPI.meta.credits("By ZXMushroom63"); if (e.message.toLowerCase().startsWith(".reload_tex")) { e.preventDefault = true; ModAPI.mc.renderItem.itemModelMesher.simpleShapesCache.clear(); - ModAPI.promisify(ModAPI.mc.refreshResources)().then(()=>{ + ModAPI.promisify(ModAPI.mc.refreshResources)().then(() => { ModAPI.events.callEvent("custom:asyncsink_reloaded", {}); }); } From 1b8323557c60980ebe7fffe88b5016d6dd5960f1 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Mon, 20 Jan 2025 01:45:14 +0800 Subject: [PATCH 05/41] reload RenderItem on .reload_tex --- examplemods/AsyncSink.js | 1 + 1 file changed, 1 insertion(+) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index fdaa0a9..3fbb992 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -267,6 +267,7 @@ ModAPI.meta.credits("By ZXMushroom63"); ModAPI.mc.renderItem.itemModelMesher.simpleShapesCache.clear(); ModAPI.promisify(ModAPI.mc.refreshResources)().then(() => { ModAPI.events.callEvent("custom:asyncsink_reloaded", {}); + ModAPI.events.callEvent("lib:asyncsink:registeritems", ModAPI.mc.renderItem); }); } }); From 8b1787b25d8b945684d3632e7b303bc8341ac876 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Mon, 20 Jan 2025 02:13:49 +0800 Subject: [PATCH 06/41] fix gun mod issue + auto texture reloading on blocks --- docs/tutorials/custom_block.md | 2 +- examplemods/Tutorial_Custom_Block.js | 2 +- examplemods/block_of_steve_advanced.js | 2 +- examplemods/block_of_steve_simple.js | 2 +- examplemods/guns.js | 2 ++ examplemods/guns_craftable.js | 2 ++ examplemods/unlucky_blocks.js | 2 +- 7 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/tutorials/custom_block.md b/docs/tutorials/custom_block.md index e4e9aa0..682c242 100644 --- a/docs/tutorials/custom_block.md +++ b/docs/tutorials/custom_block.md @@ -185,7 +185,7 @@ When it's loaded, we'll: ModAPI.addEventListener("lib:asyncsink", async () => { //Add an asyncronous listener to AsyncSink loading. ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{ //when asyncsink yells at us to register the custom block, register it - renderItem.registerItem(custom_block, ModAPI.util.str("custom_block")); + renderItem.registerBlock(custom_block, ModAPI.util.str("custom_block")); }); AsyncSink.L10N.set("tile.custom_block.name", "My Custom Block"); //Set the name of the block diff --git a/examplemods/Tutorial_Custom_Block.js b/examplemods/Tutorial_Custom_Block.js index fced749..157524d 100644 --- a/examplemods/Tutorial_Custom_Block.js +++ b/examplemods/Tutorial_Custom_Block.js @@ -92,7 +92,7 @@ ModAPI.addEventListener("lib:asyncsink", async () => { //Add an asyncronous listener to AsyncSink loading. ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{ - renderItem.registerItem(custom_block, ModAPI.util.str("custom_block")); + renderItem.registerBlock(custom_block, ModAPI.util.str("custom_block")); }); AsyncSink.L10N.set("tile.custom_block.name", "My Custom Block"); //Set the name of the block diff --git a/examplemods/block_of_steve_advanced.js b/examplemods/block_of_steve_advanced.js index 5a5fbe3..b90bcd4 100644 --- a/examplemods/block_of_steve_advanced.js +++ b/examplemods/block_of_steve_advanced.js @@ -43,7 +43,7 @@ function registerSteveClientSide() { itemClass.staticMethods.registerItemBlock0.method(block_of_steve); ModAPI.addEventListener("lib:asyncsink", async () => { ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{ - renderItem.registerItem(block_of_steve, ModAPI.util.str("steve")); + renderItem.registerBlock(block_of_steve, ModAPI.util.str("steve")); }); AsyncSink.L10N.set("tile.steve.name", "Block Of Steve"); AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/steve.json", JSON.stringify( diff --git a/examplemods/block_of_steve_simple.js b/examplemods/block_of_steve_simple.js index a3cf599..1c48a19 100644 --- a/examplemods/block_of_steve_simple.js +++ b/examplemods/block_of_steve_simple.js @@ -34,7 +34,7 @@ function registerSteveClientSide() { ModAPI.addEventListener("lib:asyncsink", async () => { ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{ - renderItem.registerItem(block_of_steve, ModAPI.util.str("steve")); + renderItem.registerBlock(block_of_steve, ModAPI.util.str("steve")); }); AsyncSink.L10N.set("tile.steve.name", "Block Of Steve"); AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/steve.json", JSON.stringify( diff --git a/examplemods/guns.js b/examplemods/guns.js index 5bca369..215d926 100644 --- a/examplemods/guns.js +++ b/examplemods/guns.js @@ -5,6 +5,8 @@ ModAPI.meta.icon(itemTexture); ModAPI.meta.description("Requires AsyncSink."); + ModAPI.require("player"); + function PistolItem() { var recoilSpeed = 0; //recoil controller var DamageSourceClass = ModAPI.reflect.getClassByName("DamageSource"); diff --git a/examplemods/guns_craftable.js b/examplemods/guns_craftable.js index 9dc6286..e0275e7 100644 --- a/examplemods/guns_craftable.js +++ b/examplemods/guns_craftable.js @@ -5,6 +5,8 @@ ModAPI.meta.icon(itemTexture); ModAPI.meta.description("Requires AsyncSink."); + ModAPI.require("player"); + function PistolItem() { var recoilSpeed = 0; //recoil controller var DamageSourceClass = ModAPI.reflect.getClassByName("DamageSource"); diff --git a/examplemods/unlucky_blocks.js b/examplemods/unlucky_blocks.js index ba048a0..90e613c 100644 --- a/examplemods/unlucky_blocks.js +++ b/examplemods/unlucky_blocks.js @@ -87,7 +87,7 @@ var block_of_unluckiness = UnluckyBlocks(); ModAPI.addEventListener("lib:asyncsink", async () => { ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{ - renderItem.registerItem(block_of_unluckiness, ModAPI.util.str("unluckiness")); + renderItem.registerBlock(block_of_unluckiness, ModAPI.util.str("unluckiness")); }); AsyncSink.L10N.set("tile.unluckiness.name", "Unlucky Block"); AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/unluckiness.json", JSON.stringify( From 2c8bb5b6e0f4a3756d5e10034c7ca9bcfa71ecfb Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Mon, 20 Jan 2025 21:18:50 +0800 Subject: [PATCH 07/41] entity keygen function --- postinit.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/postinit.js b/postinit.js index 344d338..b586040 100644 --- a/postinit.js +++ b/postinit.js @@ -1055,4 +1055,8 @@ globalThis.modapi_postinit = "(" + (() => { var values = [...ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry.$modapi_specmap.values()]; return qhash(block, values); } + ModAPI.keygen.entity = function (entity) { + var values = ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticVariables.idToClassMapping.$elementData.data.filter(x=>x).map(x=>x.$key.$value); + return qhash(entity, values); + } }).toString() + ")();"; From db4183a523f8bd647f8cf7a78154c67d40859d05 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Mon, 20 Jan 2025 21:21:01 +0800 Subject: [PATCH 08/41] fix typo --- postinit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postinit.js b/postinit.js index b586040..bb3ffa6 100644 --- a/postinit.js +++ b/postinit.js @@ -1020,7 +1020,7 @@ globalThis.modapi_postinit = "(" + (() => { ModAPI.util.getIdFromBlock = easyStaticMethod("net.minecraft.block.Block", "getIdFromBlock", true); function qhash(txt, arr) { - var interval = 4095; //used to be 4095 - arr.length, but that increases incompatibility based on load order and otehr circumstances + var interval = 4095; //used to be 4095 - arr.length, but that increases incompatibility based on load order and other circumstances if (arr.length >= 4095) { console.error("[ModAPI.keygen] Ran out of IDs while generating for " + txt); return -1; From e7133938d7e6e04ae5162b6f34e218dd48fd8e30 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Mon, 20 Jan 2025 23:14:25 +0800 Subject: [PATCH 09/41] add js class to java class operator --- docs/apidoc/utils.md | 5 ++++- postinit.js | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/apidoc/utils.md b/docs/apidoc/utils.md index 054469e..cead964 100644 --- a/docs/apidoc/utils.md +++ b/docs/apidoc/utils.md @@ -81,4 +81,7 @@ Methods: - `ModAPI.util.getBlockById(id: number) : Block` - Gets a block by it's ID - `ModAPI.util.getBlockFromItem(item: Item) : Block` - - Gets a block from an ItemBlock instance. \ No newline at end of file + - Gets a block from an ItemBlock instance. +- `ModAPI.util.asClass(class: Class) : Class` + - Converts a javascript class to a java class. + - Equivalent of using `MyClass.class` in java. \ No newline at end of file diff --git a/postinit.js b/postinit.js index bb3ffa6..f745446 100644 --- a/postinit.js +++ b/postinit.js @@ -166,6 +166,8 @@ globalThis.modapi_postinit = "(" + (() => { return name; } + ModAPI.util.asClass = ModAPI.util.asClass = ModAPI.hooks._teavm.$rt_cls; + ModAPI.util.wrap = function (outputValue, target, corrective, disableFunctions) { target ||= {}; corrective ||= false; From c484a187183ce3e8824b635a2c7c201c2b872cf6 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Mon, 20 Jan 2025 23:15:03 +0800 Subject: [PATCH 10/41] start custom entity code --- examplemods/cubeentity.js | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 examplemods/cubeentity.js diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js new file mode 100644 index 0000000..f9b1dd5 --- /dev/null +++ b/examplemods/cubeentity.js @@ -0,0 +1,3 @@ +(function CubeEntity() { + +})(); \ No newline at end of file From c8664c7a5b2133a7074f8fac1804420e209b6611 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Mon, 20 Jan 2025 23:29:51 +0800 Subject: [PATCH 11/41] start on custom entities --- examplemods/CustomItemsDemo.js | 2 +- examplemods/cubeentity.js | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/examplemods/CustomItemsDemo.js b/examplemods/CustomItemsDemo.js index e062cc7..19b9b31 100644 --- a/examplemods/CustomItemsDemo.js +++ b/examplemods/CustomItemsDemo.js @@ -1,4 +1,4 @@ -//This mod also requires lib.customitems.js +//Demo mod showing how to use lib.customitems.js ModAPI.addEventListener("lib:libcustomitems:loaded", () => { console.log("Registered my cool custom item."); LibCustomItems.registerItem({ diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js index f9b1dd5..8594633 100644 --- a/examplemods/cubeentity.js +++ b/examplemods/cubeentity.js @@ -1,3 +1,24 @@ (function CubeEntity() { - + ModAPI.meta.title("Cube Entity"); + ModAPI.meta.version("v0"); + ModAPI.meta.description("testing custom entities"); + ModAPI.meta.credits("By ZXMushroom64"); + + function registerEntity() { + + + return { + EntityCube: null, + ModelCube: null, + RenderCube: null + } + } + + ModAPI.dedicatedServer.appendCode(registerEntity); + var data = registerEntity(); + + ModAPI.addEventListener("lib:asyncsink", async () => { + //ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(wellGetThere), new data.RenderCube(ModAPI.mc.renderManager.getRef())) + AsyncSink.L10N.set("entity.Cube.name", "Cube (TM)"); + }); })(); \ No newline at end of file From 9555af5b1acdad7dd2a2f1fce19a6ec2c52a6361 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sat, 25 Jan 2025 17:26:41 +0800 Subject: [PATCH 12/41] progress on custom entities --- docs/apidoc/keygen.md | 6 ++- docs/apidoc/reflect.md | 5 ++- examplemods/cubeentity.js | 87 ++++++++++++++++++++++++++++++++++++--- postinit.js | 4 +- 4 files changed, 91 insertions(+), 11 deletions(-) diff --git a/docs/apidoc/keygen.md b/docs/apidoc/keygen.md index 17c5cd9..1e3a401 100644 --- a/docs/apidoc/keygen.md +++ b/docs/apidoc/keygen.md @@ -1,8 +1,10 @@ ## ModAPI.keygen -ModAPI.keygen contains the API for getting item and block IDs from a string. It looks at the registries for items and blocks to derive the IDs, so IDs will not be automatically reserved until a block/item is actually registered. Ideally, you'd want to call a keygen method just before registering your block. +ModAPI.keygen contains the API for getting numerical item, block and entity IDs from a string. It looks at registries to derive the IDs, so IDs will not be automatically reserved until a block/item is actually registered into the game. Ideally, you'd want to call a keygen method just before registering your block. Methods: - `ModAPI.keygen.item(itemId: String) : number` - Example usage is: `var id = ModAPI.keygen.item("my_example_item");` - `ModAPI.keygen.block(blockId: String) : number` - - Example usage is: `var id = ModAPI.keygen.block("my_example_block");` \ No newline at end of file + - Example usage is: `var id = ModAPI.keygen.block("my_example_block");` +- `ModAPI.keygen.entity(entityId: String) : number` + - Example usage is: `var id = ModAPI.keygen.entity("my_example_entity");` \ No newline at end of file diff --git a/docs/apidoc/reflect.md b/docs/apidoc/reflect.md index b8bdd88..0aff6cc 100644 --- a/docs/apidoc/reflect.md +++ b/docs/apidoc/reflect.md @@ -16,9 +16,10 @@ Methods: - This method is used to find a class by its id. - For example, to get the `Minecraft` class, you can use `ModAPI.reflect.getClassById("Minecraft")` - This runs slower than `getClassById` because it has to filter through all classes. Make sure to cache the result rather than calling it over and over again. -- `ModAPI.reflect.getSuper(rClass: ReflectClass, filter: Function) : Function` +- `ModAPI.reflect.getSuper(rClass: ReflectClass, filter: Function?) : Function` - Gets a super function from a reflect class. This is used to extend built in classes, like `Block`. - - For an example, see lines [29](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L29) and [33](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L33) in unlucky_blocks.js + - For an example, see lines [29](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L29) and [33](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L33) in `unlucky_blocks.js` + - When called without a filter function, the filter defaults to `(fn)=>fn.length === 1` - `ModAPI.reflect.prototypeStack(rClass: ReflectClass, target: Class/ConstructorFunction) : void` - Copies methods from a reflect class and it's parents onto a target native JavaScript class. This allows TeaVM to use these objects normally, without you having to manually reimplement every method. In other words, this is the equivalent of extending a class. - [Example usage](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L37) diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js index 8594633..1dba60b 100644 --- a/examplemods/cubeentity.js +++ b/examplemods/cubeentity.js @@ -5,12 +5,87 @@ ModAPI.meta.credits("By ZXMushroom64"); function registerEntity() { - + // Utils + const ResourceLocation = ModAPI.reflect.getClassByName("ResourceLocation").constructors.find(x=>x.length===1); + const GlStateManager = Object.fromEntries(Object.values(ModAPI.reflect.getClassByName("GlStateManager").staticMethods).map(x=>[x.methodNameShort, x.method])); + + // START CUSTOM ENTITY + var entityClass = ModAPI.reflect.getClassById("net.minecraft.entity.Entity"); + var entitySuper = ModAPI.reflect.getSuper(entityClass, (x) => x.length === 2); + var nme_EntityCube = function nme_EntityCube($worldIn) { + entitySuper(this, $worldIn); + this.$setCreativeTab(creativeMiscTab); + this.$preventEntitySpawning = 1; + this.$setSize(1, 1); + } + ModAPI.reflect.prototypeStack(entityClass, nme_EntityCube); + nme_EntityCube.prototype.$canTriggerWalking = function () { return 0 }; + nme_EntityCube.prototype.$canBePushed = function () { return 0 }; + nme_EntityCube.prototype.$getCollisionBox = function () { return this.$getEntityBoundingBox() }; + nme_EntityCube.prototype.$getCollisionBoundingBox = function () { return this.$getEntityBoundingBox() }; + // END CUSTOM ENTITY + + + // START CUSTOM MODEL + var ModelRenderer = ModAPI.reflect.getClassById("net.minecraft.client.model.ModelRenderer").constructors.find(x => x.length === 1); + var modelBaseClass = ModAPI.reflect.getClassById("net.minecraft.client.model.ModelBase"); + var modelBaseSuper = ModAPI.reflect.getSuper(entityClass); //while super isn't used when extending this class, java implies the call. + var nmcm_ModelCube = function nmcm_ModelCube() { + modelBaseSuper(this); + this.$textureWidth = 64; + this.$textureHeight = 64; + this.$cubeRenderer = ModelRenderer(this).$setTextureOffset(0, 0); + this.$cubeRenderer.$addBox0(0, 0, 0, 1, 1, 1); + this.$cubeRenderer.$setRotationPoint(0, 0, 0); + } + ModAPI.reflect.prototypeStack(modelBaseClass, nmcm_ModelCube); + nmcm_ModelCube.prototype.$render = function ($entity, useless1, useless2, partialTicks, useless3, useless4, f) { + this.$cubeRenderer.$render(f); + } + // END CUSTOM MODEL + + + // START CUSTOM RENDERER + var renderClass = ModAPI.reflect.getClassById("net.minecraft.client.renderer.entity.Render"); + var renderSuper = ModAPI.reflect.getSuper(entityClass, (x)=>x.length === 2); + const cubeTextures = ResourceLocation(ModAPI.util.str("textures/entity/cube.png")); + var nmcre_RenderCube = function nmcre_RenderCube(renderManager) { + renderSuper(this, renderManager); + this.$modelCube = new nmcm_ModelCube(); + this.$shadowSize = 0.5; + } + ModAPI.reflect.prototypeStack(renderClass, nmcre_RenderCube); + nmcre_RenderCube.prototype.$getEntityTexture = function (entity) { + return cubeTextures; + } + const parentDoRender = nmcre_RenderCube.prototype.$doRender; + nmcre_RenderCube.prototype.$doRender = function (entity, x, y, z, yaw, pitch) { + GlStateManager.pushMatrix(); + GlStateManager.translate(x, y + 0.25, z); + GlStateManager.rotate(180 - yaw, 0, 1, 0); + this.$bindEntityTexture(entity); + this.$modelCube.$render(entity, 0, 0, -0.1, 0, 0, 0.0625); + GlStateManager.popMatrix(); + parentDoRender.apply(this, [entity, x, y, z, yaw, pitch]); + } + + ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticMethods.addMapping0( + ModAPI.util.asClass(nme_EntityCube), + { + $createEntity: nme_EntityCube + }, + ModAPI.util.str("Cube"), + ModAPI.keygen.entity("cube"), + 0x000000, //egg base + 0x00FF00 //egg spots + ); + ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(nme_EntityCube), new nmcre_RenderCube(ModAPI.mc.renderManager.getRef())); return { - EntityCube: null, - ModelCube: null, - RenderCube: null + EntityCube: nme_EntityCube, + ModelCube: nmcm_ModelCube, + RenderCube: nmcre_RenderCube, + cubeTexture: cubeTextures } } @@ -18,7 +93,9 @@ var data = registerEntity(); ModAPI.addEventListener("lib:asyncsink", async () => { - //ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(wellGetThere), new data.RenderCube(ModAPI.mc.renderManager.getRef())) AsyncSink.L10N.set("entity.Cube.name", "Cube (TM)"); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/entity/cube.png", await (await fetch( + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAQBJREFUeF7l0BFzAmAAgOGvKxgMgiAYDIJgEARBEASDQTAIgiAYBEEQBN0NBkEQBEEQBIMgCAZBEAwGgyAIgiAIgiConxE88PJ790RCCNdYCOGeRe/4j4SYDvCgAzzqAHEdIKEDJHWAJx3gWQdI6QBpHeBFB8joAFkdIKcD5HWAgg5Q1AFedYA3HaCkA7zrAGUdoKIDVHWAmg7woQPUdYCGDtDUAVo6QFsH6OgAnzrAlw7Q1QF6OkBfBxjoAEMdYKQDjHWAiQ7wrQNMdYCZDjDXAX50gIUOsNQBVjrArw7wpwP86wBrHWCjA2x1gJ0OsNcBDjrAUQc46QBnHeBiA9wALSueIjTE4PwAAAAASUVORK5CYII=" + )).arrayBuffer()); }); })(); \ No newline at end of file diff --git a/postinit.js b/postinit.js index f745446..9c35081 100644 --- a/postinit.js +++ b/postinit.js @@ -166,7 +166,7 @@ globalThis.modapi_postinit = "(" + (() => { return name; } - ModAPI.util.asClass = ModAPI.util.asClass = ModAPI.hooks._teavm.$rt_cls; + ModAPI.util.asClass = ModAPI.hooks._teavm.$rt_cls; ModAPI.util.wrap = function (outputValue, target, corrective, disableFunctions) { target ||= {}; @@ -413,7 +413,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 ||= (x) => x.length === 1; var initialiser = reflectClass.internalConstructors.find(filter); return function superFunction(thisArg, ...extra_args) { reflectClass.class.call(thisArg); From bbed8f1f42f1fb50c4d4204018541c6b2f747cbf Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sat, 25 Jan 2025 17:48:01 +0800 Subject: [PATCH 13/41] prototype stack changes --- docs/apidoc/reflect.md | 1 + postinit.js | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/apidoc/reflect.md b/docs/apidoc/reflect.md index 0aff6cc..c6b2770 100644 --- a/docs/apidoc/reflect.md +++ b/docs/apidoc/reflect.md @@ -22,6 +22,7 @@ Methods: - When called without a filter function, the filter defaults to `(fn)=>fn.length === 1` - `ModAPI.reflect.prototypeStack(rClass: ReflectClass, target: Class/ConstructorFunction) : void` - Copies methods from a reflect class and it's parents onto a target native JavaScript class. This allows TeaVM to use these objects normally, without you having to manually reimplement every method. In other words, this is the equivalent of extending a class. + - Also adds some metadata to make the class work with `ModAPI.util.asClass` - [Example usage](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L37) ### ReflectClass Definition diff --git a/postinit.js b/postinit.js index 9c35081..64c3e9b 100644 --- a/postinit.js +++ b/postinit.js @@ -429,6 +429,7 @@ globalThis.modapi_postinit = "(" + (() => { item: null, supertypes: [reflectClass.class] }; + classFn.classObject = null; } var reloadDeprecationWarnings = 0; From f5d9cdac88d07b9c575115c113758e08f4b2589a Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sat, 25 Jan 2025 17:58:45 +0800 Subject: [PATCH 14/41] push cube entity code (still very broken) --- examplemods/cubeentity.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js index 1dba60b..37ab0e5 100644 --- a/examplemods/cubeentity.js +++ b/examplemods/cubeentity.js @@ -29,7 +29,7 @@ // START CUSTOM MODEL var ModelRenderer = ModAPI.reflect.getClassById("net.minecraft.client.model.ModelRenderer").constructors.find(x => x.length === 1); var modelBaseClass = ModAPI.reflect.getClassById("net.minecraft.client.model.ModelBase"); - var modelBaseSuper = ModAPI.reflect.getSuper(entityClass); //while super isn't used when extending this class, java implies the call. + var modelBaseSuper = ModAPI.reflect.getSuper(modelBaseClass); //while super isn't used when extending this class, java implies the call. var nmcm_ModelCube = function nmcm_ModelCube() { modelBaseSuper(this); this.$textureWidth = 64; @@ -47,7 +47,7 @@ // START CUSTOM RENDERER var renderClass = ModAPI.reflect.getClassById("net.minecraft.client.renderer.entity.Render"); - var renderSuper = ModAPI.reflect.getSuper(entityClass, (x)=>x.length === 2); + var renderSuper = ModAPI.reflect.getSuper(renderClass, (x)=>x.length === 2); const cubeTextures = ResourceLocation(ModAPI.util.str("textures/entity/cube.png")); var nmcre_RenderCube = function nmcre_RenderCube(renderManager) { renderSuper(this, renderManager); @@ -69,7 +69,7 @@ parentDoRender.apply(this, [entity, x, y, z, yaw, pitch]); } - ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticMethods.addMapping0( + ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticMethods.addMapping0.method( ModAPI.util.asClass(nme_EntityCube), { $createEntity: nme_EntityCube @@ -79,7 +79,7 @@ 0x000000, //egg base 0x00FF00 //egg spots ); - ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(nme_EntityCube), new nmcre_RenderCube(ModAPI.mc.renderManager.getRef())); + return { EntityCube: nme_EntityCube, @@ -93,6 +93,7 @@ var data = registerEntity(); ModAPI.addEventListener("lib:asyncsink", async () => { + ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(nme_EntityCube), new nmcre_RenderCube(ModAPI.mc.renderManager.getRef())); AsyncSink.L10N.set("entity.Cube.name", "Cube (TM)"); AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/entity/cube.png", await (await fetch( "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAQBJREFUeF7l0BFzAmAAgOGvKxgMgiAYDIJgEARBEASDQTAIgiAYBEEQBN0NBkEQBEEQBIMgCAZBEAwGgyAIgiAIgiConxE88PJ790RCCNdYCOGeRe/4j4SYDvCgAzzqAHEdIKEDJHWAJx3gWQdI6QBpHeBFB8joAFkdIKcD5HWAgg5Q1AFedYA3HaCkA7zrAGUdoKIDVHWAmg7woQPUdYCGDtDUAVo6QFsH6OgAnzrAlw7Q1QF6OkBfBxjoAEMdYKQDjHWAiQ7wrQNMdYCZDjDXAX50gIUOsNQBVjrArw7wpwP86wBrHWCjA2x1gJ0OsNcBDjrAUQc46QBnHeBiA9wALSueIjTE4PwAAAAASUVORK5CYII=" From e15b0891b51f71e3252b0998f924532361f5a73a Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sat, 25 Jan 2025 18:13:20 +0800 Subject: [PATCH 15/41] (feat: entities) broke it, but its better than before --- examplemods/cubeentity.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js index 37ab0e5..2261f27 100644 --- a/examplemods/cubeentity.js +++ b/examplemods/cubeentity.js @@ -72,7 +72,9 @@ ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticMethods.addMapping0.method( ModAPI.util.asClass(nme_EntityCube), { - $createEntity: nme_EntityCube + $createEntity: function ($worldIn) { + return new nme_EntityCube($worldIn); + } }, ModAPI.util.str("Cube"), ModAPI.keygen.entity("cube"), @@ -93,10 +95,10 @@ var data = registerEntity(); ModAPI.addEventListener("lib:asyncsink", async () => { - ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(nme_EntityCube), new nmcre_RenderCube(ModAPI.mc.renderManager.getRef())); AsyncSink.L10N.set("entity.Cube.name", "Cube (TM)"); AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/entity/cube.png", await (await fetch( "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAQBJREFUeF7l0BFzAmAAgOGvKxgMgiAYDIJgEARBEASDQTAIgiAYBEEQBN0NBkEQBEEQBIMgCAZBEAwGgyAIgiAIgiConxE88PJ790RCCNdYCOGeRe/4j4SYDvCgAzzqAHEdIKEDJHWAJx3gWQdI6QBpHeBFB8joAFkdIKcD5HWAgg5Q1AFedYA3HaCkA7zrAGUdoKIDVHWAmg7woQPUdYCGDtDUAVo6QFsH6OgAnzrAlw7Q1QF6OkBfBxjoAEMdYKQDjHWAiQ7wrQNMdYCZDjDXAX50gIUOsNQBVjrArw7wpwP86wBrHWCjA2x1gJ0OsNcBDjrAUQc46QBnHeBiA9wALSueIjTE4PwAAAAASUVORK5CYII=" )).arrayBuffer()); + ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(nme_EntityCube), new nmcre_RenderCube(ModAPI.mc.renderManager.getRef())); }); })(); \ No newline at end of file From 0e583514448f7b31689e73ca60782a3ac936b5f2 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sat, 25 Jan 2025 19:45:32 +0800 Subject: [PATCH 16/41] some fixes --- examplemods/cubeentity.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js index 2261f27..19ece96 100644 --- a/examplemods/cubeentity.js +++ b/examplemods/cubeentity.js @@ -23,6 +23,7 @@ nme_EntityCube.prototype.$canBePushed = function () { return 0 }; nme_EntityCube.prototype.$getCollisionBox = function () { return this.$getEntityBoundingBox() }; nme_EntityCube.prototype.$getCollisionBoundingBox = function () { return this.$getEntityBoundingBox() }; + nme_EntityCube.prototype.$entityInit = function () { console.log("Cube entity created!") }; // Needed, is an abstract method in parent class // END CUSTOM ENTITY @@ -99,6 +100,7 @@ AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/entity/cube.png", await (await fetch( "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAQBJREFUeF7l0BFzAmAAgOGvKxgMgiAYDIJgEARBEASDQTAIgiAYBEEQBN0NBkEQBEEQBIMgCAZBEAwGgyAIgiAIgiConxE88PJ790RCCNdYCOGeRe/4j4SYDvCgAzzqAHEdIKEDJHWAJx3gWQdI6QBpHeBFB8joAFkdIKcD5HWAgg5Q1AFedYA3HaCkA7zrAGUdoKIDVHWAmg7woQPUdYCGDtDUAVo6QFsH6OgAnzrAlw7Q1QF6OkBfBxjoAEMdYKQDjHWAiQ7wrQNMdYCZDjDXAX50gIUOsNQBVjrArw7wpwP86wBrHWCjA2x1gJ0OsNcBDjrAUQc46QBnHeBiA9wALSueIjTE4PwAAAAASUVORK5CYII=" )).arrayBuffer()); - ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(nme_EntityCube), new nmcre_RenderCube(ModAPI.mc.renderManager.getRef())); + + ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(data.EntityCube), new data.RenderCube(ModAPI.mc.renderManager.getRef())); }); })(); \ No newline at end of file From b56d38c1741f33033545e19870ea94504706385b Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 26 Jan 2025 14:12:09 +0800 Subject: [PATCH 17/41] a couple of fixes --- examplemods/cubeentity.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js index 19ece96..17ef42e 100644 --- a/examplemods/cubeentity.js +++ b/examplemods/cubeentity.js @@ -6,16 +6,15 @@ function registerEntity() { // Utils - const ResourceLocation = ModAPI.reflect.getClassByName("ResourceLocation").constructors.find(x=>x.length===1); - const GlStateManager = Object.fromEntries(Object.values(ModAPI.reflect.getClassByName("GlStateManager").staticMethods).map(x=>[x.methodNameShort, x.method])); + const ResourceLocation = ModAPI.reflect.getClassByName("ResourceLocation").constructors.find(x => x.length === 1); + const GlStateManager = Object.fromEntries(Object.values(ModAPI.reflect.getClassByName("GlStateManager").staticMethods).map(x => [x.methodNameShort, x.method])); // START CUSTOM ENTITY var entityClass = ModAPI.reflect.getClassById("net.minecraft.entity.Entity"); var entitySuper = ModAPI.reflect.getSuper(entityClass, (x) => x.length === 2); var nme_EntityCube = function nme_EntityCube($worldIn) { entitySuper(this, $worldIn); - this.$setCreativeTab(creativeMiscTab); - this.$preventEntitySpawning = 1; + //this.$preventEntitySpawning = 1; this.$setSize(1, 1); } ModAPI.reflect.prototypeStack(entityClass, nme_EntityCube); @@ -23,6 +22,13 @@ nme_EntityCube.prototype.$canBePushed = function () { return 0 }; nme_EntityCube.prototype.$getCollisionBox = function () { return this.$getEntityBoundingBox() }; nme_EntityCube.prototype.$getCollisionBoundingBox = function () { return this.$getEntityBoundingBox() }; + nme_EntityCube.prototype.$readEntityFromNBT = function (nbtTagCompount) { // Needed, is an abstract method in parent class + nbtTagCompount = ModAPI.util.wrap(nbtTagCompount); + }; + nme_EntityCube.prototype.$writeEntityToNBT = function (nbtTagCompount) { // Needed, is an abstract method in parent class + nbtTagCompount = ModAPI.util.wrap(nbtTagCompount); + }; + nme_EntityCube.prototype.$getCollisionBoundingBox = function () { return this.$getEntityBoundingBox() }; // Needed, is an abstract method in parent class nme_EntityCube.prototype.$entityInit = function () { console.log("Cube entity created!") }; // Needed, is an abstract method in parent class // END CUSTOM ENTITY @@ -48,7 +54,7 @@ // START CUSTOM RENDERER var renderClass = ModAPI.reflect.getClassById("net.minecraft.client.renderer.entity.Render"); - var renderSuper = ModAPI.reflect.getSuper(renderClass, (x)=>x.length === 2); + var renderSuper = ModAPI.reflect.getSuper(renderClass, (x) => x.length === 2); const cubeTextures = ResourceLocation(ModAPI.util.str("textures/entity/cube.png")); var nmcre_RenderCube = function nmcre_RenderCube(renderManager) { renderSuper(this, renderManager); @@ -82,7 +88,7 @@ 0x000000, //egg base 0x00FF00 //egg spots ); - + return { EntityCube: nme_EntityCube, @@ -100,7 +106,8 @@ AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/entity/cube.png", await (await fetch( "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAQBJREFUeF7l0BFzAmAAgOGvKxgMgiAYDIJgEARBEASDQTAIgiAYBEEQBN0NBkEQBEEQBIMgCAZBEAwGgyAIgiAIgiConxE88PJ790RCCNdYCOGeRe/4j4SYDvCgAzzqAHEdIKEDJHWAJx3gWQdI6QBpHeBFB8joAFkdIKcD5HWAgg5Q1AFedYA3HaCkA7zrAGUdoKIDVHWAmg7woQPUdYCGDtDUAVo6QFsH6OgAnzrAlw7Q1QF6OkBfBxjoAEMdYKQDjHWAiQ7wrQNMdYCZDjDXAX50gIUOsNQBVjrArw7wpwP86wBrHWCjA2x1gJ0OsNcBDjrAUQc46QBnHeBiA9wALSueIjTE4PwAAAAASUVORK5CYII=" )).arrayBuffer()); - + ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(data.EntityCube), new data.RenderCube(ModAPI.mc.renderManager.getRef())); }); + console.log(data); })(); \ No newline at end of file From 66386b44370de7d75c28b7fe12c35814561af82f Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 26 Jan 2025 14:28:06 +0800 Subject: [PATCH 18/41] fix array bug --- postinit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/postinit.js b/postinit.js index 64c3e9b..6a0cab4 100644 --- a/postinit.js +++ b/postinit.js @@ -189,9 +189,9 @@ globalThis.modapi_postinit = "(" + (() => { if (!disableFunctions && outputValue && typeof outputValue === "function" && target) { return function (...args) { var xOut = outputValue.apply(target, args); - if (xOut && typeof xOut === "object" && Array.isArray(xOut.data) && typeof outputValue.type === "function") { + if (xOut && typeof xOut === "object" && Array.isArray(xOut.data) && typeof xOut.type === "function") { if (corrective) { - return new Proxy(outputValue.data, CorrectiveArray); + return new Proxy(xOut.data, CorrectiveArray); } return new Proxy(xOut.data, ModAPI.util.TeaVMArray_To_Recursive_BaseData_ProxyConf); } From bb880e2cf3a489801883ed746f95765faec638aa Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 26 Jan 2025 16:01:58 +0800 Subject: [PATCH 19/41] make id a seperate constant, add L10N serverside --- examplemods/cubeentity.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js index 17ef42e..b4d4c90 100644 --- a/examplemods/cubeentity.js +++ b/examplemods/cubeentity.js @@ -75,7 +75,7 @@ GlStateManager.popMatrix(); parentDoRender.apply(this, [entity, x, y, z, yaw, pitch]); } - + const ID = ModAPI.keygen.entity("cube"); ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticMethods.addMapping0.method( ModAPI.util.asClass(nme_EntityCube), { @@ -84,11 +84,15 @@ } }, ModAPI.util.str("Cube"), - ModAPI.keygen.entity("cube"), + ID, 0x000000, //egg base 0x00FF00 //egg spots ); + ModAPI.addEventListener("lib:asyncsink", async () => { + AsyncSink.L10N.set("entity.Cube.name", "Cube (TM)"); + }); + return { EntityCube: nme_EntityCube, @@ -102,7 +106,6 @@ var data = registerEntity(); ModAPI.addEventListener("lib:asyncsink", async () => { - AsyncSink.L10N.set("entity.Cube.name", "Cube (TM)"); AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/entity/cube.png", await (await fetch( "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAQBJREFUeF7l0BFzAmAAgOGvKxgMgiAYDIJgEARBEASDQTAIgiAYBEEQBN0NBkEQBEEQBIMgCAZBEAwGgyAIgiAIgiConxE88PJ790RCCNdYCOGeRe/4j4SYDvCgAzzqAHEdIKEDJHWAJx3gWQdI6QBpHeBFB8joAFkdIKcD5HWAgg5Q1AFedYA3HaCkA7zrAGUdoKIDVHWAmg7woQPUdYCGDtDUAVo6QFsH6OgAnzrAlw7Q1QF6OkBfBxjoAEMdYKQDjHWAiQ7wrQNMdYCZDjDXAX50gIUOsNQBVjrArw7wpwP86wBrHWCjA2x1gJ0OsNcBDjrAUQc46QBnHeBiA9wALSueIjTE4PwAAAAASUVORK5CYII=" )).arrayBuffer()); From bd4b5b9d39ad8474b3b577ac8a8f15081fe306da Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 26 Jan 2025 16:02:27 +0800 Subject: [PATCH 20/41] log entity ID for potential mismatch --- examplemods/cubeentity.js | 1 + 1 file changed, 1 insertion(+) diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js index b4d4c90..4c39650 100644 --- a/examplemods/cubeentity.js +++ b/examplemods/cubeentity.js @@ -88,6 +88,7 @@ 0x000000, //egg base 0x00FF00 //egg spots ); + console.log(ID); ModAPI.addEventListener("lib:asyncsink", async () => { AsyncSink.L10N.set("entity.Cube.name", "Cube (TM)"); From 42a0f3e148d9c331f0718fddaf2aa62aa7232767 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 26 Jan 2025 19:30:47 +0800 Subject: [PATCH 21/41] add interfaces and implements --- docs/apidoc/reflect.md | 3 +++ index.html | 1 + injector.js | 35 ++++++++++++++++++++--------------- postinit.js | 21 +++++++++++++++++++-- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/docs/apidoc/reflect.md b/docs/apidoc/reflect.md index c6b2770..08e0580 100644 --- a/docs/apidoc/reflect.md +++ b/docs/apidoc/reflect.md @@ -24,6 +24,9 @@ Methods: - Copies methods from a reflect class and it's parents onto a target native JavaScript class. This allows TeaVM to use these objects normally, without you having to manually reimplement every method. In other words, this is the equivalent of extending a class. - Also adds some metadata to make the class work with `ModAPI.util.asClass` - [Example usage](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L37) +- `ModAPI.reflect.implements(target: Class/ConstructorFunction, interface: ReflectClass)` + - Marks the provided interface as a supertype of the target class. + - JavaScript equivalent of the `implements` keyword ### ReflectClass Definition Each `ReflectClass` has the following properties: diff --git a/index.html b/index.html index ffbb2b7..6032025 100644 --- a/index.html +++ b/index.html @@ -151,6 +151,7 @@ ModAPI.hooks ||= {}; ModAPI.hooks.freezeCallstack = false; ModAPI.hooks._rippedData ||= []; + ModAPI.hooks._rippedInterfaceMap ||= {}; ModAPI.hooks._teavm ||= {}; ModAPI.hooks._rippedConstructors ||= {}; ModAPI.hooks._rippedInternalConstructors ||= {}; diff --git a/injector.js b/injector.js index 7048e86..386e80b 100644 --- a/injector.js +++ b/injector.js @@ -50,13 +50,13 @@ function entriesToStaticVariableProxy(entries, prefix, clinitList) { .join(",")}]; /*/ var proxy = `ModAPI.hooks._rippedStaticProperties[\`${prefix.replace( - "var ", - "" - )}\`] = new Proxy({${entries - .flatMap((x) => { - return '"' + x.name + '"'; - }) - .join(":null,") + (entries.length > 0 ? ":null" : "") + "var ", + "" + )}\`] = new Proxy({${entries + .flatMap((x) => { + return '"' + x.name + '"'; + }) + .join(":null,") + (entries.length > 0 ? ":null" : "") }}, { get: function (a,b,c) { switch (b) { @@ -136,7 +136,7 @@ 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()); + 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..."); @@ -171,7 +171,7 @@ var main;(function(){` ); } ); - + 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 @@ -205,6 +205,7 @@ var main;(function(){` }).filter(x => { return (!x.includes("$_clinit_$")) && (!x.includes("$lambda$")) }); + //Also stores classes from $rt_classWithoutFields(0) patchedFile = patchedFile.replaceAll( /var \S+?_\S+? = \$rt_classWithoutFields\(\S*?\);/gm, function (match) { @@ -213,6 +214,7 @@ var main;(function(){` "" ); var entries = []; + staticVariables.forEach((entry) => { if (entry.startsWith(prefix)) { var variableName = entry @@ -229,14 +231,17 @@ var main;(function(){` }); var proxy = entriesToStaticVariableProxy(entries, prefix, clinitList); - - return match + proxy; + var shortPrefix = prefix.replace( + "var ", + "" + ); + return match + `ModAPI.hooks._rippedInterfaceMap[\`${shortPrefix}\`]=${shortPrefix};` + proxy; } ); //Edge cases. sigh //Done: add support for static properties on classes with constructors like this: function nmcg_GuiMainMenu() { - + patchedFile = patchedFile.replaceAll( /function [a-z]+?_([a-zA-Z0-9\$]+?)\(\) \{/gm, (match) => { @@ -335,7 +340,7 @@ document.querySelector("#giveme").addEventListener("click", () => { } else if (globalThis.doShronk) { patchedFile = await shronk(patchedFile); } - + patchedFile.replace(`{"._|_libserverside_|_."}`, ""); var blob = new Blob([patchedFile], { type: file.type }); saveAs(blob, "processed." + fileType); @@ -356,13 +361,13 @@ document.querySelector("#givemeserver").addEventListener("click", () => { file.text().then(async (string) => { var patchedFile = string; - + if (globalThis.doEaglerforge) { patchedFile = await processClasses(patchedFile); } else if (globalThis.doShronk) { patchedFile = await shronk(patchedFile); } - + patchedFile.replace(`{"._|_libserverside_|_."}`, `(${EFServer.toString()})()`); var blob = new Blob([patchedFile], { type: file.type }); saveAs(blob, "efserver." + fileType); diff --git a/postinit.js b/postinit.js index 6a0cab4..5a6c38b 100644 --- a/postinit.js +++ b/postinit.js @@ -269,6 +269,7 @@ globalThis.modapi_postinit = "(" + (() => { ModAPI.hooks._rippedConstructorKeys = Object.keys(ModAPI.hooks._rippedConstructors); ModAPI.hooks._rippedInternalConstructorKeys = Object.keys(ModAPI.hooks._rippedInternalConstructors); ModAPI.hooks._rippedMethodKeys = Object.keys(ModAPI.hooks._rippedMethodTypeMap); + ModAPI.hooks._rippedInterfaceKeys = Object.keys(ModAPI.hooks._rippedInterfaceMap); var compiledNames = new Set(); var metaMap = {}; @@ -302,10 +303,19 @@ globalThis.modapi_postinit = "(" + (() => { } }); + ModAPI.hooks._rippedInterfaceKeys.forEach(className => { + if (typeof className === "string" && className.length > 0) { + //Interfaces using $rt_classWithoutFields(0) and no constructors. + if (className && className.includes("_")) { + compiledNames.add(className); + } + } + }); + //Initialise all compiled names into the class map compiledNames.forEach(compiledName => { - var item = metaMap[compiledName]; + var item = metaMap[compiledName] || ModAPI.hooks._rippedInterfaceMap[compiledName]; var classId = item?.$meta?.name || null; if (!ModAPI.hooks._classMap[compiledName]) { @@ -321,7 +331,7 @@ globalThis.modapi_postinit = "(" + (() => { "staticVariables": {}, "staticVariableNames": [], "class": item || null, - "hasMeta": !!item, + "hasMeta": !!(item?.$meta), "instanceOf": function (object) { try { return ModAPI.hooks._teavm.$rt_isInstance(object, item || null); @@ -431,6 +441,13 @@ globalThis.modapi_postinit = "(" + (() => { }; classFn.classObject = null; } + ModAPI.reflect.implements = function implements(classFn, reflectClass) { + classFn.$meta ||= {}; + classFn.$meta.supertypes ||= []; + if (reflectClass && reflectClass.class) { + classFn.$meta.supertypes.push(reflectClass.class); + } + } var reloadDeprecationWarnings = 0; const TeaVMArray_To_Recursive_BaseData_ProxyConf = { From 23b42e66fb7f8d3eaee3690b6322be1bfb871cc6 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 26 Jan 2025 20:13:02 +0800 Subject: [PATCH 22/41] going insane --- examplemods/cubeentity.js | 17 ++++++++++++----- injector.js | 2 +- postinit.js | 15 +++++++++------ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js index 4c39650..f84e108 100644 --- a/examplemods/cubeentity.js +++ b/examplemods/cubeentity.js @@ -2,22 +2,28 @@ ModAPI.meta.title("Cube Entity"); ModAPI.meta.version("v0"); ModAPI.meta.description("testing custom entities"); - ModAPI.meta.credits("By ZXMushroom64"); + ModAPI.meta.credits("By ZXMushroom63"); function registerEntity() { // Utils const ResourceLocation = ModAPI.reflect.getClassByName("ResourceLocation").constructors.find(x => x.length === 1); const GlStateManager = Object.fromEntries(Object.values(ModAPI.reflect.getClassByName("GlStateManager").staticMethods).map(x => [x.methodNameShort, x.method])); + const IAnimals = ModAPI.reflect.getClassById("net.minecraft.entity.passive.IAnimals"); // START CUSTOM ENTITY var entityClass = ModAPI.reflect.getClassById("net.minecraft.entity.Entity"); var entitySuper = ModAPI.reflect.getSuper(entityClass, (x) => x.length === 2); var nme_EntityCube = function nme_EntityCube($worldIn) { entitySuper(this, $worldIn); - //this.$preventEntitySpawning = 1; + this.$preventEntitySpawning = 1; this.$setSize(1, 1); } ModAPI.reflect.prototypeStack(entityClass, nme_EntityCube); + + //Turns out that minecraft 1.8's networking code is really stupid. Notch hardcoded every entity except for ones that implement the IAnimals interface. + //I don't know why, and I don't really care either. As long as it works (been working on this for too long, losing sanity) + ModAPI.reflect.implements(nme_EntityCube, IAnimals); + nme_EntityCube.prototype.$canTriggerWalking = function () { return 0 }; nme_EntityCube.prototype.$canBePushed = function () { return 0 }; nme_EntityCube.prototype.$getCollisionBox = function () { return this.$getEntityBoundingBox() }; @@ -27,9 +33,10 @@ }; nme_EntityCube.prototype.$writeEntityToNBT = function (nbtTagCompount) { // Needed, is an abstract method in parent class nbtTagCompount = ModAPI.util.wrap(nbtTagCompount); - }; - nme_EntityCube.prototype.$getCollisionBoundingBox = function () { return this.$getEntityBoundingBox() }; // Needed, is an abstract method in parent class - nme_EntityCube.prototype.$entityInit = function () { console.log("Cube entity created!") }; // Needed, is an abstract method in parent class + }; + nme_EntityCube.prototype.$entityInit = function () { + console.log("Cube entity created!"); + }; // END CUSTOM ENTITY diff --git a/injector.js b/injector.js index 386e80b..30b93ce 100644 --- a/injector.js +++ b/injector.js @@ -1,4 +1,4 @@ -globalThis.ModAPIVersion = "v2.6"; +globalThis.ModAPIVersion = "v2.7"; globalThis.doEaglerforge = true; document.querySelector("title").innerText = `EaglerForge Injector ${ModAPIVersion}`; document.querySelector("h1").innerText = `EaglerForge Injector ${ModAPIVersion}`; diff --git a/postinit.js b/postinit.js index 5a6c38b..7e5fa42 100644 --- a/postinit.js +++ b/postinit.js @@ -452,13 +452,14 @@ globalThis.modapi_postinit = "(" + (() => { var reloadDeprecationWarnings = 0; const TeaVMArray_To_Recursive_BaseData_ProxyConf = { get(target, prop, receiver) { + var corrective = !!this._corrective; if (prop === "getRef") { return function () { return target; } } var outputValue = Reflect.get(target, prop, receiver); - var wrapped = ModAPI.util.wrap(outputValue, target, this._corrective, true); + var wrapped = ModAPI.util.wrap(outputValue, target, corrective, true); if (wrapped) { return wrapped; } @@ -480,6 +481,7 @@ globalThis.modapi_postinit = "(" + (() => { return ("$" + prop) in target; }, get(target, prop, receiver) { + var corrective = !!this._corrective; if (prop === "getCorrective") { return function () { return new Proxy(target, patchProxyConfToCorrective(TeaVM_to_Recursive_BaseData_ProxyConf)); @@ -487,7 +489,7 @@ globalThis.modapi_postinit = "(" + (() => { } if (prop === "isCorrective") { return function () { - return !!this._corrective; + return corrective; } } if (prop === "getRef") { @@ -504,15 +506,15 @@ globalThis.modapi_postinit = "(" + (() => { } } if (prop === "isModProxy") { - return true + return true; } var outProp = "$" + prop; - if (this._corrective) { + if (corrective) { outProp = ModAPI.util.getNearestProperty(target, outProp); } var outputValue = Reflect.get(target, outProp, receiver); - var wrapped = ModAPI.util.wrap(outputValue, target, this._corrective, false); + var wrapped = ModAPI.util.wrap(outputValue, target, corrective, false); if (wrapped) { return wrapped; } @@ -1076,7 +1078,8 @@ globalThis.modapi_postinit = "(" + (() => { return qhash(block, values); } ModAPI.keygen.entity = function (entity) { - var values = ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticVariables.idToClassMapping.$elementData.data.filter(x=>x).map(x=>x.$key.$value); + 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)); return qhash(entity, values); } }).toString() + ")();"; From b6c5003426f8a08b4808d738f657157c2c400cd8 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 26 Jan 2025 21:13:41 +0800 Subject: [PATCH 23/41] fixed bug with implements --- postinit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postinit.js b/postinit.js index 7e5fa42..d3b56f2 100644 --- a/postinit.js +++ b/postinit.js @@ -441,7 +441,7 @@ globalThis.modapi_postinit = "(" + (() => { }; classFn.classObject = null; } - ModAPI.reflect.implements = function implements(classFn, reflectClass) { + ModAPI.reflect.implements = function impl(classFn, reflectClass) { classFn.$meta ||= {}; classFn.$meta.supertypes ||= []; if (reflectClass && reflectClass.class) { From bf494e10d3ab944de46e9af3524ab254e93f4205 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Tue, 28 Jan 2025 14:18:41 +0800 Subject: [PATCH 24/41] cube entity working! --- examplemods/AsyncSink.js | 6 ++++++ examplemods/cubeentity.js | 35 +++++++++++++++++++++++------------ postinit.js | 12 ++++++------ 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index 3fbb992..394272f 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -58,6 +58,12 @@ ModAPI.meta.credits("By ZXMushroom63"); return true; } + AsyncSink.hideFile = function hideFile(path) { + AsyncSink.FSOverride.add(path); + AsyncSink.FS.delete(path); + return true; + } + AsyncSink.getFile = function getFile(path) { return AsyncSink.FS.get(path) || new ArrayBuffer(0); } diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js index f84e108..e0cd7ac 100644 --- a/examplemods/cubeentity.js +++ b/examplemods/cubeentity.js @@ -25,7 +25,7 @@ ModAPI.reflect.implements(nme_EntityCube, IAnimals); nme_EntityCube.prototype.$canTriggerWalking = function () { return 0 }; - nme_EntityCube.prototype.$canBePushed = function () { return 0 }; + nme_EntityCube.prototype.$canBePushed = function () { return 1 }; nme_EntityCube.prototype.$getCollisionBox = function () { return this.$getEntityBoundingBox() }; nme_EntityCube.prototype.$getCollisionBoundingBox = function () { return this.$getEntityBoundingBox() }; nme_EntityCube.prototype.$readEntityFromNBT = function (nbtTagCompount) { // Needed, is an abstract method in parent class @@ -49,12 +49,12 @@ this.$textureWidth = 64; this.$textureHeight = 64; this.$cubeRenderer = ModelRenderer(this).$setTextureOffset(0, 0); - this.$cubeRenderer.$addBox0(0, 0, 0, 1, 1, 1); + this.$cubeRenderer.$addBox0(0, 0, 0, 64, 64, 64); this.$cubeRenderer.$setRotationPoint(0, 0, 0); } ModAPI.reflect.prototypeStack(modelBaseClass, nmcm_ModelCube); - nmcm_ModelCube.prototype.$render = function ($entity, useless1, useless2, partialTicks, useless3, useless4, f) { - this.$cubeRenderer.$render(f); + nmcm_ModelCube.prototype.$render = function ($entity, useless1, useless2, partialTicks, useless3, useless4, degToRad) { + this.$cubeRenderer.$render(degToRad); } // END CUSTOM MODEL @@ -75,12 +75,13 @@ const parentDoRender = nmcre_RenderCube.prototype.$doRender; nmcre_RenderCube.prototype.$doRender = function (entity, x, y, z, yaw, pitch) { GlStateManager.pushMatrix(); - GlStateManager.translate(x, y + 0.25, z); - GlStateManager.rotate(180 - yaw, 0, 1, 0); - this.$bindEntityTexture(entity); - this.$modelCube.$render(entity, 0, 0, -0.1, 0, 0, 0.0625); - GlStateManager.popMatrix(); - parentDoRender.apply(this, [entity, x, y, z, yaw, pitch]); + GlStateManager.translate(x + 0.5, y, z + 0.5); + GlStateManager.rotate(180 - yaw, 0, 1, 0); + GlStateManager.scale(0.25, 0.25, 0.25); + this.$bindEntityTexture(entity); + this.$modelCube.$render(entity, 0, 0, -0.1, 0, 0, 0.0625); + GlStateManager.popMatrix(); + parentDoRender.apply(this, [entity, x, y, z, yaw, pitch]); } const ID = ModAPI.keygen.entity("cube"); ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticMethods.addMapping0.method( @@ -95,6 +96,7 @@ 0x000000, //egg base 0x00FF00 //egg spots ); + // Note that the spawn egg for this will not work, as spawn eggs only spawn entities that are a subclass of EntityLivingBase console.log(ID); ModAPI.addEventListener("lib:asyncsink", async () => { @@ -117,8 +119,17 @@ AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/entity/cube.png", await (await fetch( "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAQBJREFUeF7l0BFzAmAAgOGvKxgMgiAYDIJgEARBEASDQTAIgiAYBEEQBN0NBkEQBEEQBIMgCAZBEAwGgyAIgiAIgiConxE88PJ790RCCNdYCOGeRe/4j4SYDvCgAzzqAHEdIKEDJHWAJx3gWQdI6QBpHeBFB8joAFkdIKcD5HWAgg5Q1AFedYA3HaCkA7zrAGUdoKIDVHWAmg7woQPUdYCGDtDUAVo6QFsH6OgAnzrAlw7Q1QF6OkBfBxjoAEMdYKQDjHWAiQ7wrQNMdYCZDjDXAX50gIUOsNQBVjrArw7wpwP86wBrHWCjA2x1gJ0OsNcBDjrAUQc46QBnHeBiA9wALSueIjTE4PwAAAAASUVORK5CYII=" )).arrayBuffer()); - + AsyncSink.hideFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/entity/cube.png.mcmeta"); ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(data.EntityCube), new data.RenderCube(ModAPI.mc.renderManager.getRef())); + ModAPI.promisify(ModAPI.mc.renderEngine.bindTexture)(data.cubeTexture).then(() => { + console.log("Loaded cube texture into cache."); + }); }); console.log(data); -})(); \ No newline at end of file + window.temp1 = data; +})(); +//var cube_man = new temp1.EntityCube(ModAPI.mc.theWorld.getRef()); +//cube_man.$setPosition(-191, 74, 26); +//AsyncSink.startDebugging(); +//AsyncSink.startDebuggingFS(); +//ModAPI.mc.theWorld.spawnEntityInWorld(cube_man); \ No newline at end of file diff --git a/postinit.js b/postinit.js index d3b56f2..2e7a7a9 100644 --- a/postinit.js +++ b/postinit.js @@ -1041,9 +1041,9 @@ globalThis.modapi_postinit = "(" + (() => { ModAPI.util.getBlockFromItem = easyStaticMethod("net.minecraft.block.Block", "getBlockFromItem", true); ModAPI.util.getIdFromBlock = easyStaticMethod("net.minecraft.block.Block", "getIdFromBlock", true); - function qhash(txt, arr) { - var interval = 4095; //used to be 4095 - arr.length, but that increases incompatibility based on load order and other circumstances - if (arr.length >= 4095) { + function qhash(txt, arr, interval) { + // var interval = 4095; //used to be 4095 - arr.length, but that increases incompatibility based on load order and other circumstances + if (arr.length >= interval) { console.error("[ModAPI.keygen] Ran out of IDs while generating for " + txt); return -1; } @@ -1071,15 +1071,15 @@ globalThis.modapi_postinit = "(" + (() => { } ModAPI.keygen.item = function (item) { var values = [...ModAPI.reflect.getClassById("net.minecraft.item.Item").staticVariables.itemRegistry.$modapi_specmap.values()]; - return qhash(item, values); + return qhash(item, values, 4095); } ModAPI.keygen.block = function (block) { var values = [...ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry.$modapi_specmap.values()]; - return qhash(block, values); + return qhash(block, values, 4095); } 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)); - return qhash(entity, values); + return qhash(entity, values, 127); } }).toString() + ")();"; From b384e274ac9c263d5335b7317a1e609010654ef6 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Tue, 28 Jan 2025 19:33:56 +0800 Subject: [PATCH 25/41] add audo module to asyncsink --- examplemods/AsyncSink.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index 394272f..cc7e370 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -4,8 +4,10 @@ const asyncSinkIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAY ModAPI.meta.icon(asyncSinkIcon); ModAPI.meta.credits("By ZXMushroom63"); (function AsyncSinkFn() { + const ResourceLocation = ModAPI.reflect.getClassByName("ResourceLocation").constructors.find(x => x.length === 1); //AsyncSink is a plugin to debug and override asynchronous methods in EaglercraftX async function runtimeComponent() { + const ResourceLocation = ModAPI.reflect.getClassByName("ResourceLocation").constructors.find(x => x.length === 1); 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; @@ -285,4 +287,40 @@ ModAPI.meta.credits("By ZXMushroom63"); ModAPI.events.callEvent("lib:asyncsink:registeritems", ModAPI.util.wrap(args[0])); } + AsyncSink.Audio = {}; + AsyncSink.Audio.Category = ModAPI.reflect.getClassByName("SoundCategory").staticVariables; + AsyncSink.Audio.Objects = []; + const SoundHandler_onResourceManagerReload = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.audio.SoundHandler", "onResourceManagerReload")]; + ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.audio.SoundHandler", "onResourceManagerReload")] = function (...args) { + SoundHandler_onResourceManagerReload.apply(this, args); + var snd = ModAPI.mc.mcSoundHandler; + var registry = snd.sndRegistry.soundRegistry; + console.log("[AsyncSink] Populating sound registry hash map with " + AsyncSink.Audio.Objects.length + " sound effects."); + } + + // key = "mob.entity.say" + // values = SoundEntry[] + // category: AsyncSink.Audio.Category.* + // SoundEntry = {path: String, pitch: 1, volume: 1, streaming: false} + const SoundPoolEntry = ModAPI.reflect.getClassByName("SoundPoolEntry").constructors.find(x => x.length === 4); + const SoundEventAccessor = ModAPI.reflect.getClassByName("SoundEventAccessor").constructors.find(x => x.length === 2); + const SoundEventAccessorComposite = ModAPI.reflect.getClassByName("SoundEventAccessorComposite").constructors.find(x => x.length === 4); + AsyncSink.Audio.register = function addSfx(key, category, values) { + var snd = ModAPI.mc.mcSoundHandler; + var registry = snd.sndRegistry.soundRegistry; + var rKey = ResourceLocation(ModAPI.util.str(key)); + var soundPool = values.map(se => { + var path = ResourceLocation(ModAPI.util.str(path)); + return SoundPoolEntry(path, se.pitch, se.volume, 1 * se.streaming); + }).map(spe => { + return SoundEventAccessor(spe, 1); // 1 = weight + }); + var compositeSound = SoundEventAccessorComposite(rKey, 1, 1, category); + var compositeSoundWrapped = ModAPI.util.wrap(compositeSound); + soundPool.forEach(sound => { + compositeSoundWrapped.addSoundToEventPool(sound); + }); + AsyncSink.Audio.Objects.push([rKey, compositeSound]); + registry.$put(rKey, compositeSound); + } })(); \ No newline at end of file From 76649a062922f9aa148d20c30f1fb401ef495d46 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Tue, 28 Jan 2025 20:52:54 +0800 Subject: [PATCH 26/41] add backup superclass id --- postinit.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/postinit.js b/postinit.js index 2e7a7a9..88e9a83 100644 --- a/postinit.js +++ b/postinit.js @@ -357,8 +357,14 @@ globalThis.modapi_postinit = "(" + (() => { } } if (typeof item?.$meta?.superclass === "function" && item?.$meta?.superclass?.$meta) { - ModAPI.hooks._classMap[compiledName].superclassName = item.$meta.superclass.$meta.name; ModAPI.hooks._classMap[compiledName].superclass = item.$meta.superclass; + ModAPI.hooks._classMap[compiledName].superclassName = item.$meta.superclass.$meta.name ?? (item.$meta.superclass ? item.$meta.superclass.name.split("_").map((x, i) => { + if (i === 0) { + return x.split("").join(".") + "." + } else { + return x; + } + }).join("_").replace("._", ".") : null); } else { ModAPI.hooks._classMap[compiledName].superclass = null; ModAPI.hooks._classMap[compiledName].superclassName = null; From 6b2d19c705909de27cb6b2dc4cf5316e30e55bd5 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Wed, 29 Jan 2025 10:13:28 +0800 Subject: [PATCH 27/41] progress on audio api --- examplemods/AsyncSink.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index cc7e370..ee3b80c 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -45,6 +45,17 @@ ModAPI.meta.credits("By ZXMushroom63"); AsyncSink.L10N = new Map(); AsyncSink.FSOverride = new Set(); AsyncSink.MIDDLEWARE = []; + + //takes in a ResourceLocation and removes cached data. Use to only reload a specific texture if you know where it is stored. + AsyncSink.clearResourcePointer = function clearResourcePointer(resourceLocation) { + if (!resourceLocation) { + return; + } + var res = ModAPI.util.wrap((resourceLocation.isModProxy === true) ? resourceLocation.getRef() : resourceLocation); + res.cachedPointer = null; + res.cachedPointerType = 0; + ModAPI.mc.getTextureManager().mapTextureObjects.remove(res.getRef()); + } AsyncSink.setFile = function setFile(path, data) { if (typeof data === "string") { data = encoder.encode(data).buffer; @@ -296,6 +307,9 @@ ModAPI.meta.credits("By ZXMushroom63"); var snd = ModAPI.mc.mcSoundHandler; var registry = snd.sndRegistry.soundRegistry; console.log("[AsyncSink] Populating sound registry hash map with " + AsyncSink.Audio.Objects.length + " sound effects."); + AsyncSink.Audio.Objects.forEach(pair => { + registry.$put(pair[0], pair[1]); + }); } // key = "mob.entity.say" @@ -310,7 +324,7 @@ ModAPI.meta.credits("By ZXMushroom63"); var registry = snd.sndRegistry.soundRegistry; var rKey = ResourceLocation(ModAPI.util.str(key)); var soundPool = values.map(se => { - var path = ResourceLocation(ModAPI.util.str(path)); + var path = ResourceLocation(ModAPI.util.str(se.path)); return SoundPoolEntry(path, se.pitch, se.volume, 1 * se.streaming); }).map(spe => { return SoundEventAccessor(spe, 1); // 1 = weight From 393f6fd2e754c425fa412be3f018e25af7f75767 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Wed, 29 Jan 2025 11:32:01 +0800 Subject: [PATCH 28/41] duck mod now somewhat works --- docs/apidoc/reflect.md | 8 +- examplemods/AsyncSink.js | 29 +++++-- examplemods/DuckMod.js | 166 +++++++++++++++++++++++++++++++++++++++ postinit.js | 33 +++++--- 4 files changed, 214 insertions(+), 22 deletions(-) create mode 100644 examplemods/DuckMod.js diff --git a/docs/apidoc/reflect.md b/docs/apidoc/reflect.md index 08e0580..586342c 100644 --- a/docs/apidoc/reflect.md +++ b/docs/apidoc/reflect.md @@ -6,6 +6,7 @@ Properties: - `classes: ReflectClass[]` - `ModAPI.reflect.classes` is an array of ReflectClasses, representing (almost) every java class. +- `classMap: Map` is a map of every class. Methods: @@ -54,11 +55,8 @@ Each `ReflectClass` has the following properties: - List of all the static variable names for the class. - `staticVariables: Map` - key-value dictionary of all the static variables in a class. -- `superclass: Class?` - - The raw teavm class of the superclass. -- `superclassName: String?` - - The class id of the class's superclass. Eg: `net.minecraft.client.entity.AbstractClientPlayer` - - Will be `null` if `hasMeta` is equal to `false` +- `superclass: ReflectClass?` + - The superclass. Each `ReflectClass` has the following methods: diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index ee3b80c..f5d9666 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -317,8 +317,25 @@ ModAPI.meta.credits("By ZXMushroom63"); // category: AsyncSink.Audio.Category.* // SoundEntry = {path: String, pitch: 1, volume: 1, streaming: false} const SoundPoolEntry = ModAPI.reflect.getClassByName("SoundPoolEntry").constructors.find(x => x.length === 4); - const SoundEventAccessor = ModAPI.reflect.getClassByName("SoundEventAccessor").constructors.find(x => x.length === 2); - const SoundEventAccessorComposite = ModAPI.reflect.getClassByName("SoundEventAccessorComposite").constructors.find(x => x.length === 4); + const SoundEventAccessorClass = ModAPI.reflect.getClassByName("SoundEventAccessor").class; + function makeSoundEventAccessor(soundpoolentry, weight) { + var object = new SoundEventAccessorClass; + var wrapped = ModAPI.util.wrap(object).getCorrective(); + wrapped.entry = soundpoolentry; + wrapped.weight = weight; + return object; + } + const SoundEventAccessorCompositeClass = ModAPI.reflect.getClassByName("SoundEventAccessorComposite").class; + function makeSoundEventAccessorComposite(rKey, pitch, volume, category) { + var object = new SoundEventAccessorCompositeClass; + var wrapped = ModAPI.util.wrap(object).getCorrective(); + wrapped.soundLocation = rKey; + wrapped.eventPitch = pitch; + wrapped.eventVolume = volume; + wrapped.category = category; + wrapped.soundPool = ModAPI.hooks.methods.cgcc_Lists_newArrayList1(); + return object; + } AsyncSink.Audio.register = function addSfx(key, category, values) { var snd = ModAPI.mc.mcSoundHandler; var registry = snd.sndRegistry.soundRegistry; @@ -327,14 +344,14 @@ ModAPI.meta.credits("By ZXMushroom63"); var path = ResourceLocation(ModAPI.util.str(se.path)); return SoundPoolEntry(path, se.pitch, se.volume, 1 * se.streaming); }).map(spe => { - return SoundEventAccessor(spe, 1); // 1 = weight + return makeSoundEventAccessor(spe, 1); // 1 = weight }); - var compositeSound = SoundEventAccessorComposite(rKey, 1, 1, category); + var compositeSound = makeSoundEventAccessorComposite(rKey, 1, 1, category); var compositeSoundWrapped = ModAPI.util.wrap(compositeSound); soundPool.forEach(sound => { - compositeSoundWrapped.addSoundToEventPool(sound); + compositeSoundWrapped.soundPool.add(sound); }); AsyncSink.Audio.Objects.push([rKey, compositeSound]); - registry.$put(rKey, compositeSound); + registry.put(rKey, compositeSound); } })(); \ No newline at end of file diff --git a/examplemods/DuckMod.js b/examplemods/DuckMod.js new file mode 100644 index 0000000..39d2c36 --- /dev/null +++ b/examplemods/DuckMod.js @@ -0,0 +1,166 @@ +(function DuckEntity() { + ModAPI.meta.title("Duck Mod"); + ModAPI.meta.version("v1"); + ModAPI.meta.description("adds ducks to the game"); + ModAPI.meta.credits("By ZXMushroom63"); + + function registerEntity() { + // Utils + function AITask(name, length) { + return ModAPI.reflect.getClassById("net.minecraft.entity.ai." + name).constructors.find(x => x.length === length); + } + const ResourceLocation = ModAPI.reflect.getClassByName("ResourceLocation").constructors.find(x => x.length === 1); + const EntityPlayer = ModAPI.reflect.getClassByName("EntityPlayer"); + const GlStateManager = Object.fromEntries(Object.values(ModAPI.reflect.getClassByName("GlStateManager").staticMethods).map(x => [x.methodNameShort, x.method])); + const SharedMonsterAttributes = ModAPI.reflect.getClassByName("SharedMonsterAttributes").staticVariables; + + // START CUSTOM ENTITY + var entityClass = ModAPI.reflect.getClassById("net.minecraft.entity.passive.EntityAnimal"); + var entitySuper = ModAPI.reflect.getSuper(entityClass, (x) => x.length === 2); + var nme_EntityDuck = function nme_EntityDuck($worldIn) { + entitySuper(this, $worldIn); + this.$setSize(0.4, 0.7); + this.$tasks.$addTask(0, AITask("EntityAISwimming", 1)(this)); + this.$tasks.$addTask(1, AITask("EntityAIPanic", 2)(this, 1.9)); + this.$tasks.$addTask(2, AITask("EntityAIMate", 2)(this, 1.0)); + this.$tasks.$addTask(3, AITask("EntityAITempt", 4)(this, 1.5, ModAPI.items.bread.getRef(), 0)); //won't cause a problem as the bread is obtained when the entity is constructed. + this.$tasks.$addTask(4, AITask("EntityAIFollowParent", 2)(this, 1.2)); + this.$tasks.$addTask(5, AITask("EntityAIWander", 2)(this, 1.1)); + this.$tasks.$addTask(6, AITask("EntityAIWatchClosest", 3)(this, ModAPI.util.asClass(EntityPlayer.class), 6)); + this.$tasks.$addTask(7, AITask("EntityAILookIdle", 1)(this)); + } + ModAPI.reflect.prototypeStack(entityClass, nme_EntityDuck); + nme_EntityDuck.prototype.$getEyeHeight = function () { + return this.$height; + } + + const originalApplyEntityAttributes = nme_EntityDuck.prototype.$applyEntityAttributes; + nme_EntityDuck.prototype.$applyEntityAttributes = function () { + originalApplyEntityAttributes.apply(this, []); + this.$getEntityAttribute(SharedMonsterAttributes.maxHealth).$setBaseValue(5); + this.$getEntityAttribute(SharedMonsterAttributes.movementSpeed).$setBaseValue(0.25); + } + + const originalLivingUpdate = nme_EntityDuck.prototype.$onLivingUpdate; + nme_EntityDuck.prototype.$onLivingUpdate = function () { + originalLivingUpdate.apply(this, []); + if (this.$isInWater()) { + this.$getEntityAttribute(SharedMonsterAttributes.movementSpeed).$setBaseValue(0.6); + } else { + this.$getEntityAttribute(SharedMonsterAttributes.movementSpeed).$setBaseValue(0.25); + } + } + + nme_EntityDuck.prototype.$getLivingSound = function () { + return ModAPI.util.str("mob.duck.quack"); + } + nme_EntityDuck.prototype.$getHurtSound = function () { + return ModAPI.util.str("mob.duck.quack"); + } + nme_EntityDuck.prototype.$getDeathSound = function () { + return ModAPI.util.str("mob.duck.quack"); + } + nme_EntityDuck.prototype.$playStepSound = function () { + this.$playSound("mob.duck.step", 0.2, 1); + } + nme_EntityDuck.prototype.$getDropItem = function () { + return ModAPI.items.feather.getRef(); + } + nme_EntityDuck.prototype.$createChild = function (otherParent) { + return new nme_EntityDuck(this.$worldObj); + } + nme_EntityDuck.prototype.$isBreedingItem = function (itemstack) { + return itemstack !== null && itemstack.getItem() === ModAPI.items.bread.getRef(); + } + // END CUSTOM ENTITY + + + // START CUSTOM MODEL + var modelChickenClass = ModAPI.reflect.getClassById("net.minecraft.client.model.ModelChicken"); + var modelChickenSuper = ModAPI.reflect.getSuper(modelChickenClass); //while super isn't used when extending this class, java implies the call. + var nmcm_ModelDuck = function nmcm_ModelDuck() { + modelChickenSuper(this); + } + ModAPI.reflect.prototypeStack(modelChickenClass, nmcm_ModelDuck); + // END CUSTOM MODEL + + + // START CUSTOM RENDERER + var renderClass = ModAPI.reflect.getClassById("net.minecraft.client.renderer.entity.RenderLiving"); + var renderSuper = ModAPI.reflect.getSuper(renderClass, (x) => x.length === 4); + const duckTextures = ResourceLocation(ModAPI.util.str("textures/entity/duck.png")); + var nmcre_RenderDuck = function nmcre_RenderDuck(renderManager, modelBaseIn, shadowSizeIn) { + renderSuper(this, renderManager, modelBaseIn, shadowSizeIn); + } + ModAPI.reflect.prototypeStack(renderClass, nmcre_RenderDuck); + nmcre_RenderDuck.prototype.$getEntityTexture = function (entity) { + return duckTextures; + } + + const ID = ModAPI.keygen.entity("duck"); + ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticMethods.addMapping0.method( + ModAPI.util.asClass(nme_EntityDuck), + { + $createEntity: function ($worldIn) { + return new nme_EntityDuck($worldIn); + } + }, + ModAPI.util.str("Duck"), + ID, + 0x5e3e2d, //egg base + 0x269166 //egg spots + ); + // Note that the spawn egg for this will not work, as spawn eggs only spawn entities that are a subclass of EntityLivingBase + console.log(ID); + + ModAPI.addEventListener("lib:asyncsink", async () => { + AsyncSink.L10N.set("entity.Duck.name", "Duck"); + }); + + + return { + EntityDuck: nme_EntityDuck, + ModelDuck: nmcm_ModelDuck, + RenderDuck: nmcre_RenderDuck, + duckTextures: duckTextures + } + } + + ModAPI.dedicatedServer.appendCode(registerEntity); + var data = registerEntity(); + + ModAPI.addEventListener("lib:asyncsink", async () => { + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/entity/duck.png", await (await fetch( + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgBAMAAABQs2O3AAAAAXNSR0IB2cksfwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAACdQTFRFAAAALJBrKYdk1pcLp3UI4JEzL5pyaFY4AAAANa6BW0AjYUQlVTwhXohvWAAAAA10Uk5TAP///////////////y0EQa0AAADJSURBVHicY2RgVPj/gIGBwZhBkGEPkGb9zYACGBmYEv4tADKKj/CCFYT+3oCmgE3g1weQAQwQIzAUJLIyMACF9MA8kBEMASgqGIXYGl8ArbiPEEJToMbg8AdIz8OpIJGBFSTwCqcCIQYGfgb8VjDYoFqBChiBeDWYFYpbwSkwywy3ArDvGVyoZMVuVwJWrApDV7AaiQO05YwJuoJTSBwzbFbsQeK4UGzFIwaGf0wMDCE4rQDGGuN/BgZTnFZAFbiRZgUDATCqAAIA8Z45IRCQkrIAAAAASUVORK5CYII=" + )).arrayBuffer()); + AsyncSink.hideFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/entity/duck.png.mcmeta"); + ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(data.EntityDuck), new data.RenderDuck(ModAPI.mc.renderManager.getRef(), new data.ModelDuck(), 0.3)); + ModAPI.promisify(ModAPI.mc.renderEngine.bindTexture)(data.duckTextures).then(() => { + console.log("Loaded duck texture into cache."); + }); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/sounds/mob/duck/quack.ogg", await (await fetch( + "data:audio/ogg;base64,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Mdaaa869p9ZijLHWmnPuvQAAkwcHAKgEG2dYSTorHA0uNGQlAJAbAIAgxJhzzkEIIYQQQgghUoox5yCEEEIIIZRSSqQUY85BCCGEEEIIIYSMMeeggxBCCKWUUkopGWPOQQghhBBKKKWEEjrnoIMQQgmllFJKKaV0zjkIIYQQSimllFJK6SCEEEIIpZRSSimllNJBCCGEUEoppZRSSiklhBBCCKWUUkoppZRSSgghhBBKKaWUUkoppZQQQgillFJKKaWUUkopIYQQSimllFJKKaWUUkIIpZRSSimllFJKKaWEEEoppZRSSimllFJKCaGUUkoppZRSSimllBJKKaWUUkoppZRSSikllFJKKaWUUkoppZRSSiillFJKKaWUUkoppZRQSimllFJKKaWUUkopoZRSSimllFJKKaWUUgoAADpwAAAIMKLSQuw048ojcEQhwwRUaMhKACAcAABABDoIIYQQQggRcxBCCCGEEEKImIMQQgghhBBCCCGEEEIIpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppRQAdZnhABg9YeMMK0lnhaPBhYasBADSAgAAYxhjjCnIpLMWY60NYxBCB52EFGqoJaaGMQghdFBKSi22WHMGoaRSSkktxliDzT2DUEoppaQWY605F+NBSCWl1GKrteccjO4glJJSSjHWmnPuvWjQSUmptVpz7j0HXzwIpaTWWow9Bx+MMKKUlmKssdYcfBFGGFFKSy3GmnvNvRhjhEopxlp7zrnnXIwRPqUWY6659x58LsL44mLMOffigw8+CGGMkDHm2HPwvRdjjA/CyFxzLsIY44swwvggbK25B1+MEUYYY3zvNfigezHCCCOMMcII3XPRRfhijDFGGF+EAQC5EQ4AiAtGElJnGVYaceMJGCKQQkNWAQAxAAAEMcYgpJBSSinFGGOMMcYYY4wxxhhjjDHGnGPOOeecAADABAcAgAAr2JVZWrVR3NRJXvRB4BM6YjMy5FIqZnIi6JEaarES7NAKbvACsNCQlQAAGQAA5KSUlFotGkLKQWk1iMgg5STFJCJjkILSgqeQMYhJyh1jCiEFqXbQMYUUoxpSCplS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" + )).arrayBuffer()); + AsyncSink.Audio.register("mob.duck.quack", AsyncSink.Audio.Category.ANIMAL, [ + { + path: "sounds/mob/duck/quack.ogg", + pitch: 1, + volume: 1, + streaming: false //use for large audio files + } + ]); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/sounds/mob/duck/step.ogg", await (await fetch( + "data:audio/ogg;base64,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LbZYa60159RajDHWmmvOvafWYoyx1ppz7r0AAJMHBwCoBBtnWEk6KxwNLjRkJQCQGwCAIMSYc85BCCGEEEIIIVKKMecghBBCCCGUUkqkFGPOQQghhBBCCCGEjDHnoIMQQgillFJKKRljzkEIIYQQSiilhBI656CDEEIJpZRSSimldM45CCGEEEoppZRSSukghBBCCKWUUkoppZTSQQghhFBKKaWUUkopJYQQQgillFJKKaWUUkoIIYQQSimllFJKKaWUEEIIpZRSSimllFJKKSGEEEoppZRSSimllFJCCKWUUkoppZRSSimlhBBKKaWUUkoppZRSSgmhlFJKKaWUUkoppZQSSimllFJKKaWUUkopJZRSSimllFJKKaWUUkoopZRSSimllFJKKaWUUEoppZRSSimllFJKKaGUUkoppZRSSimllFIKAAA6cAAACDCi0kLsNOPKI3BEIcMEVGjISgAgHAAAQAQ6CCGEEEIIEXMQQgghhBBCiJiDEEIIIYQQQgghhBBCCKWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaUUAHWZ4QAYPWHjDCtJZ4WjwYWGrAQA0gIAAGMYY4wpyKSzFmOtDWMQQgedhBRqqCWmhjEIIXRQSkottlhzBqGkUkpJLcZYg809g1BKKaWkFmOtORfjQUglpdRiq7XnHIzuIJSSUkox1ppz7r1o0ElJqbVac+49B188CKWk1lqMPQcfjDCilJZirLHWHHwRRhhRSkstxpp7zb0YY4RKKcZae86551yMET6lFmOuufcefC7C+OJizDn34oMPPghhjJAx5thz8L0XY4wPwshccy7CGOOLMML4IGytuQdfjBFGGGN87zX4oHsxwggjjDHCCN1z0UX4YowxRhhfhAEAuREOAIgLRhJSZxlWGnHjCRgikEJDVgEAMQAABDHGIKSQUkopxRhjjDHGGGOMMcYYY4wxxpxjzjnnnAAAwAQHAIAAK9iVWVq1UdzUSV70QeATOmIzMuRSKmZyIuiRGmqxEuzQCm7wArDQkJUAABkAAOSklJRaLRpCykFpNYjIIOUkxSQiY5CC0oKnkDGIScodYwohBal2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" + )).arrayBuffer()); + AsyncSink.Audio.register("mob.duck.step", AsyncSink.Audio.Category.ANIMAL, [ + { + path: "sounds/mob/duck/step.ogg", + pitch: 1, + volume: 1, + streaming: false //use for large audio files + } + ]); + }); + console.log(data); + window.temp1 = data; +})(); \ No newline at end of file diff --git a/postinit.js b/postinit.js index 88e9a83..cc58aea 100644 --- a/postinit.js +++ b/postinit.js @@ -353,21 +353,14 @@ globalThis.modapi_postinit = "(" + (() => { return this.constructors[i]; } } - } + }, } } + if (typeof item?.$meta?.superclass === "function" && item?.$meta?.superclass?.$meta) { - ModAPI.hooks._classMap[compiledName].superclass = item.$meta.superclass; - ModAPI.hooks._classMap[compiledName].superclassName = item.$meta.superclass.$meta.name ?? (item.$meta.superclass ? item.$meta.superclass.name.split("_").map((x, i) => { - if (i === 0) { - return x.split("").join(".") + "." - } else { - return x; - } - }).join("_").replace("._", ".") : null); + ModAPI.hooks._classMap[compiledName].superclassRaw = item.$meta.superclass; } else { - ModAPI.hooks._classMap[compiledName].superclass = null; - ModAPI.hooks._classMap[compiledName].superclassName = null; + ModAPI.hooks._classMap[compiledName].superclassRaw = null; } if (item?.["$$constructor$$"]) { @@ -414,7 +407,19 @@ globalThis.modapi_postinit = "(" + (() => { })); ModAPI.hooks._classMap[compiledName].staticVariableNames = Object.keys(ModAPI.hooks._classMap[compiledName].staticVariables); }); + + //populate superclasses + compiledNames.forEach(compiledName => { + var item = ModAPI.hooks._classMap[compiledName]; + if (item.superclassRaw) { + item.superclass = ModAPI.hooks._classMap[item.superclassRaw.name]; + } else { + item.superclass = null; + } + }); + ModAPI.reflect.classes = Object.values(ModAPI.hooks._classMap); + ModAPI.reflect.classMap = ModAPI.hooks._classMap; console.log("[ModAPI] Regenerated hook classmap."); } ModAPI.hooks.regenerateClassMap(); @@ -430,7 +435,13 @@ 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 ||= (x) => x.length === 1; + while (!reflectClass.internalConstructors.find(filter) && reflectClass.superclass) { + reflectClass = reflectClass.superclass; + } var initialiser = reflectClass.internalConstructors.find(filter); + if (!initialiser) { + throw new Error("[ModAPI] Failed to find matching superclass constructor in tree."); + } return function superFunction(thisArg, ...extra_args) { reflectClass.class.call(thisArg); initialiser(thisArg, ...extra_args); From 9f9d91f6a31d1e582c419f120ac2a639702e8012 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Thu, 30 Jan 2025 17:44:07 +0800 Subject: [PATCH 29/41] fix corrective proxy setters --- postinit.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/postinit.js b/postinit.js index cc58aea..da9d73c 100644 --- a/postinit.js +++ b/postinit.js @@ -538,7 +538,11 @@ globalThis.modapi_postinit = "(" + (() => { return outputValue; }, set(object, prop, value) { + var corrective = !!this._corrective; var outProp = "$" + prop; + if (corrective) { + outProp = ModAPI.util.getNearestProperty(target, outProp); + } object[outProp] = value; return true; }, From 64745a86f7c63cef16c127425ee57a708832efb9 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Thu, 30 Jan 2025 17:46:56 +0800 Subject: [PATCH 30/41] fix corrective proxy setters again --- postinit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postinit.js b/postinit.js index da9d73c..1c9ac14 100644 --- a/postinit.js +++ b/postinit.js @@ -541,7 +541,7 @@ globalThis.modapi_postinit = "(" + (() => { var corrective = !!this._corrective; var outProp = "$" + prop; if (corrective) { - outProp = ModAPI.util.getNearestProperty(target, outProp); + outProp = ModAPI.util.getNearestProperty(object, outProp); } object[outProp] = value; return true; From 9dc2ec270bddd4789d30a4628eaebf91718cf54a Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Thu, 30 Jan 2025 18:28:48 +0800 Subject: [PATCH 31/41] fix asyncsink audio --- examplemods/AsyncSink.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index f5d9666..3c29f91 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -7,7 +7,6 @@ ModAPI.meta.credits("By ZXMushroom63"); const ResourceLocation = ModAPI.reflect.getClassByName("ResourceLocation").constructors.find(x => x.length === 1); //AsyncSink is a plugin to debug and override asynchronous methods in EaglercraftX async function runtimeComponent() { - const ResourceLocation = ModAPI.reflect.getClassByName("ResourceLocation").constructors.find(x => x.length === 1); 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; @@ -317,6 +316,7 @@ ModAPI.meta.credits("By ZXMushroom63"); // category: AsyncSink.Audio.Category.* // SoundEntry = {path: String, pitch: 1, volume: 1, streaming: false} const SoundPoolEntry = ModAPI.reflect.getClassByName("SoundPoolEntry").constructors.find(x => x.length === 4); + const EaglercraftRandom = ModAPI.reflect.getClassByName("EaglercraftRandom").constructors.find(x=>x.length===0); const SoundEventAccessorClass = ModAPI.reflect.getClassByName("SoundEventAccessor").class; function makeSoundEventAccessor(soundpoolentry, weight) { var object = new SoundEventAccessorClass; @@ -334,9 +334,13 @@ ModAPI.meta.credits("By ZXMushroom63"); wrapped.eventVolume = volume; wrapped.category = category; wrapped.soundPool = ModAPI.hooks.methods.cgcc_Lists_newArrayList1(); + wrapped.rnd = EaglercraftRandom(); return object; } AsyncSink.Audio.register = function addSfx(key, category, values) { + if (!category) { + throw new Error("[AsyncSink] Invalid audio category provided: "+category); + } var snd = ModAPI.mc.mcSoundHandler; var registry = snd.sndRegistry.soundRegistry; var rKey = ResourceLocation(ModAPI.util.str(key)); @@ -353,5 +357,6 @@ ModAPI.meta.credits("By ZXMushroom63"); }); AsyncSink.Audio.Objects.push([rKey, compositeSound]); registry.put(rKey, compositeSound); + values.map(x=>"resourcepacks/AsyncSinkLib/assets/minecraft/" + x.path + ".mcmeta").forEach(x=>AsyncSink.setFile(x, new ArrayBuffer(0))); } })(); \ No newline at end of file From 5dcc60fab1791a560992920ae6aa181c7aa0b69b Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Thu, 30 Jan 2025 18:35:47 +0800 Subject: [PATCH 32/41] do not apply audio if thread is in a critical state --- examplemods/AsyncSink.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index 3c29f91..fee2f96 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -303,6 +303,9 @@ ModAPI.meta.credits("By ZXMushroom63"); const SoundHandler_onResourceManagerReload = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.audio.SoundHandler", "onResourceManagerReload")]; ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.audio.SoundHandler", "onResourceManagerReload")] = function (...args) { SoundHandler_onResourceManagerReload.apply(this, args); + if (ModAPI.util.isCritical()) { + return; + } var snd = ModAPI.mc.mcSoundHandler; var registry = snd.sndRegistry.soundRegistry; console.log("[AsyncSink] Populating sound registry hash map with " + AsyncSink.Audio.Objects.length + " sound effects."); From b33f435bf2eab6bcb84b979b9951f7818f09f20d Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Thu, 30 Jan 2025 18:38:01 +0800 Subject: [PATCH 33/41] remove $ prefix on audio registry put --- examplemods/AsyncSink.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index fee2f96..abb02a2 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -310,7 +310,7 @@ ModAPI.meta.credits("By ZXMushroom63"); var registry = snd.sndRegistry.soundRegistry; console.log("[AsyncSink] Populating sound registry hash map with " + AsyncSink.Audio.Objects.length + " sound effects."); AsyncSink.Audio.Objects.forEach(pair => { - registry.$put(pair[0], pair[1]); + registry.put(pair[0], pair[1]); }); } From 83ed8f85bd10cf0f3bfb410555b947eb6ecfb456 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Thu, 30 Jan 2025 20:46:59 +0800 Subject: [PATCH 34/41] almost working duck mod --- examplemods/DuckMod.js | 53 ++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/examplemods/DuckMod.js b/examplemods/DuckMod.js index 39d2c36..39a8efb 100644 --- a/examplemods/DuckMod.js +++ b/examplemods/DuckMod.js @@ -5,6 +5,7 @@ ModAPI.meta.credits("By ZXMushroom63"); function registerEntity() { + ModAPI.hooks.methods.jl_String_format = ModAPI.hooks.methods.nlev_HString_format; //temporary thing to fix an issue in eaglercraft // Utils function AITask(name, length) { return ModAPI.reflect.getClassById("net.minecraft.entity.ai." + name).constructors.find(x => x.length === length); @@ -19,36 +20,40 @@ var entitySuper = ModAPI.reflect.getSuper(entityClass, (x) => x.length === 2); var nme_EntityDuck = function nme_EntityDuck($worldIn) { entitySuper(this, $worldIn); - this.$setSize(0.4, 0.7); - this.$tasks.$addTask(0, AITask("EntityAISwimming", 1)(this)); - this.$tasks.$addTask(1, AITask("EntityAIPanic", 2)(this, 1.9)); - this.$tasks.$addTask(2, AITask("EntityAIMate", 2)(this, 1.0)); - this.$tasks.$addTask(3, AITask("EntityAITempt", 4)(this, 1.5, ModAPI.items.bread.getRef(), 0)); //won't cause a problem as the bread is obtained when the entity is constructed. - this.$tasks.$addTask(4, AITask("EntityAIFollowParent", 2)(this, 1.2)); - this.$tasks.$addTask(5, AITask("EntityAIWander", 2)(this, 1.1)); - this.$tasks.$addTask(6, AITask("EntityAIWatchClosest", 3)(this, ModAPI.util.asClass(EntityPlayer.class), 6)); - this.$tasks.$addTask(7, AITask("EntityAILookIdle", 1)(this)); + this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); + this.wrapped.setSize(0.4, 0.7); + this.wrapped.tasks.addTask(0, AITask("EntityAISwimming", 1)(this)); + this.wrapped.tasks.addTask(1, AITask("EntityAIPanic", 2)(this, 1.9)); + this.wrapped.tasks.addTask(2, AITask("EntityAIMate", 2)(this, 1.0)); + this.wrapped.tasks.addTask(3, AITask("EntityAITempt", 4)(this, 1.5, ModAPI.items.bread.getRef(), 0)); //won't cause a problem as the bread is obtained when the entity is constructed. + this.wrapped.tasks.addTask(4, AITask("EntityAIFollowParent", 2)(this, 1.2)); + this.wrapped.tasks.addTask(5, AITask("EntityAIWander", 2)(this, 1.1)); + this.wrapped.tasks.addTask(6, AITask("EntityAIWatchClosest", 3)(this, ModAPI.util.asClass(EntityPlayer.class), 6)); + this.wrapped.tasks.addTask(7, AITask("EntityAILookIdle", 1)(this)); } ModAPI.reflect.prototypeStack(entityClass, nme_EntityDuck); nme_EntityDuck.prototype.$getEyeHeight = function () { - return this.$height; + this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); + return this.wrapped.height; } const originalApplyEntityAttributes = nme_EntityDuck.prototype.$applyEntityAttributes; nme_EntityDuck.prototype.$applyEntityAttributes = function () { + this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); originalApplyEntityAttributes.apply(this, []); - this.$getEntityAttribute(SharedMonsterAttributes.maxHealth).$setBaseValue(5); - this.$getEntityAttribute(SharedMonsterAttributes.movementSpeed).$setBaseValue(0.25); + this.wrapped.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(5); + this.wrapped.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.25); } const originalLivingUpdate = nme_EntityDuck.prototype.$onLivingUpdate; nme_EntityDuck.prototype.$onLivingUpdate = function () { + this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); originalLivingUpdate.apply(this, []); - if (this.$isInWater()) { - this.$getEntityAttribute(SharedMonsterAttributes.movementSpeed).$setBaseValue(0.6); - } else { - this.$getEntityAttribute(SharedMonsterAttributes.movementSpeed).$setBaseValue(0.25); - } + // if (this.$isInWater()) { + // this.$getEntityAttribute(SharedMonsterAttributes.movementSpeed).$setBaseValue(0.6); + // } else { + // this.$getEntityAttribute(SharedMonsterAttributes.movementSpeed).$setBaseValue(0.25); + // } } nme_EntityDuck.prototype.$getLivingSound = function () { @@ -61,16 +66,19 @@ return ModAPI.util.str("mob.duck.quack"); } nme_EntityDuck.prototype.$playStepSound = function () { - this.$playSound("mob.duck.step", 0.2, 1); + this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); + debugger; + this.wrapped.playSound(ModAPI.util.str("mob.duck.step"), 0.2, 1); } nme_EntityDuck.prototype.$getDropItem = function () { return ModAPI.items.feather.getRef(); } nme_EntityDuck.prototype.$createChild = function (otherParent) { - return new nme_EntityDuck(this.$worldObj); + this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); + return new nme_EntityDuck(this.wrapped.worldObj); } nme_EntityDuck.prototype.$isBreedingItem = function (itemstack) { - return itemstack !== null && itemstack.getItem() === ModAPI.items.bread.getRef(); + return itemstack !== null && itemstack.$getItem() === ModAPI.items.bread.getRef(); } // END CUSTOM ENTITY @@ -141,7 +149,7 @@ AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/sounds/mob/duck/quack.ogg", await (await fetch( "data:audio/ogg;base64,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Mdaaa869p9ZijLHWmnPuvQAAkwcHAKgEG2dYSTorHA0uNGQlAJAbAIAgxJhzzkEIIYQQQgghUoox5yCEEEIIIZRSSqQUY85BCCGEEEIIIYSMMeeggxBCCKWUUkopGWPOQQghhBBKKKWEEjrnoIMQQgmllFJKKaV0zjkIIYQQSimllFJK6SCEEEIIpZRSSimllNJBCCGEUEoppZRSSiklhBBCCKWUUkoppZRSSgghhBBKKaWUUkoppZQQQgillFJKKaWUUkopIYQQSimllFJKKaWUUkIIpZRSSimllFJKKaWEEEoppZRSSimllFJKCaGUUkoppZRSSimllBJKKaWUUkoppZRSSikllFJKKaWUUkoppZRSSiillFJKKaWUUkoppZRQSimllFJKKaWUUkopoZRSSimllFJKKaWUUgoAADpwAAAIMKLSQuw048ojcEQhwwRUaMhKACAcAABABDoIIYQQQggRcxBCCCGEEEKImIMQQgghhBBCCCGEEEIIpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppRQAdZnhABg9YeMMK0lnhaPBhYasBADSAgAAYxhjjCnIpLMWY60NYxBCB52EFGqoJaaGMQghdFBKSi22WHMGoaRSSkktxliDzT2DUEoppaQWY605F+NBSCWl1GKrteccjO4glJJSSjHWmnPuvWjQSUmptVpz7j0HXzwIpaTWWow9Bx+MMKKUlmKssdYcfBFGGFFKSy3GmnvNvRhjhEopxlp7zrnnXIwRPqUWY6659x58LsL44mLMOffigw8+CGGMkDHm2HPwvRdjjA/CyFxzLsIY44swwvggbK25B1+MEUYYY3zvNfigezHCCCOMMcII3XPRRfhijDFGGF+EAQC5EQ4AiAtGElJnGVYaceMJGCKQQkNWAQAxAAAEMcYgpJBSSinFGGOMMcYYY4wxxhhjjDHGnGPOOeecAADABAcAgAAr2JVZWrVR3NRJXvRB4BM6YjMy5FIqZnIi6JEaarES7NAKbvACsNCQlQAAGQAA5KSUlFotGkLKQWk1iMgg5STFJCJjkILSgqeQMYhJyh1jCiEFqXbQMYUUoxpSCplS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" )).arrayBuffer()); - AsyncSink.Audio.register("mob.duck.quack", AsyncSink.Audio.Category.ANIMAL, [ + AsyncSink.Audio.register("mob.duck.quack", AsyncSink.Audio.Category.ANIMALS, [ { path: "sounds/mob/duck/quack.ogg", pitch: 1, @@ -152,7 +160,7 @@ AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/sounds/mob/duck/step.ogg", await (await fetch( "data:audio/ogg;base64,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LbZYa60159RajDHWmmvOvafWYoyx1ppz7r0AAJMHBwCoBBtnWEk6KxwNLjRkJQCQGwCAIMSYc85BCCGEEEIIIVKKMecghBBCCCGUUkqkFGPOQQghhBBCCCGEjDHnoIMQQgillFJKKRljzkEIIYQQSiilhBI656CDEEIJpZRSSimldM45CCGEEEoppZRSSukghBBCCKWUUkoppZTSQQghhFBKKaWUUkopJYQQQgillFJKKaWUUkoIIYQQSimllFJKKaWUEEIIpZRSSimllFJKKSGEEEoppZRSSimllFJCCKWUUkoppZRSSimlhBBKKaWUUkoppZRSSgmhlFJKKaWUUkoppZQSSimllFJKKaWUUkopJZRSSimllFJKKaWUUkoopZRSSimllFJKKaWUUEoppZRSSimllFJKKaGUUkoppZRSSimllFIKAAA6cAAACDCi0kLsNOPKI3BEIcMEVGjISgAgHAAAQAQ6CCGEEEIIEXMQQgghhBBCiJiDEEIIIYQQQgghhBBCCKWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaUUAHWZ4QAYPWHjDCtJZ4WjwYWGrAQA0gIAAGMYY4wpyKSzFmOtDWMQQgedhBRqqCWmhjEIIXRQSkottlhzBqGkUkpJLcZYg809g1BKKaWkFmOtORfjQUglpdRiq7XnHIzuIJSSUkox1ppz7r1o0ElJqbVac+49B188CKWk1lqMPQcfjDCilJZirLHWHHwRRhhRSkstxpp7zb0YY4RKKcZae86551yMET6lFmOuufcefC7C+OJizDn34oMPPghhjJAx5thz8L0XY4wPwshccy7CGOOLMML4IGytuQdfjBFGGGN87zX4oHsxwggjjDHCCN1z0UX4YowxRhhfhAEAuREOAIgLRhJSZxlWGnHjCRgikEJDVgEAMQAABDHGIKSQUkopxRhjjDHGGGOMMcYYY4wxxpxjzjnnnAAAwAQHAIAAK9iVWVq1UdzUSV70QeATOmIzMuRSKmZyIuiRGmqxEuzQCm7wArDQkJUAABkAAOSklJRaLRpCykFpNYjIIOUkxSQiY5CC0oKnkDGIScodYwohBal2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" )).arrayBuffer()); - AsyncSink.Audio.register("mob.duck.step", AsyncSink.Audio.Category.ANIMAL, [ + AsyncSink.Audio.register("mob.duck.step", AsyncSink.Audio.Category.ANIMALS, [ { path: "sounds/mob/duck/step.ogg", pitch: 1, @@ -162,5 +170,4 @@ ]); }); console.log(data); - window.temp1 = data; })(); \ No newline at end of file From 26eb25ed0428b0be695e9a1ece9da3cd2ffc3026 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Fri, 31 Jan 2025 15:30:31 +0800 Subject: [PATCH 35/41] duck mod fixes i think --- examplemods/DuckMod.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examplemods/DuckMod.js b/examplemods/DuckMod.js index 39a8efb..c1f2592 100644 --- a/examplemods/DuckMod.js +++ b/examplemods/DuckMod.js @@ -21,6 +21,7 @@ var nme_EntityDuck = function nme_EntityDuck($worldIn) { entitySuper(this, $worldIn); this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); + console.log(this.$setSize); this.wrapped.setSize(0.4, 0.7); this.wrapped.tasks.addTask(0, AITask("EntityAISwimming", 1)(this)); this.wrapped.tasks.addTask(1, AITask("EntityAIPanic", 2)(this, 1.9)); @@ -67,7 +68,6 @@ } nme_EntityDuck.prototype.$playStepSound = function () { this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); - debugger; this.wrapped.playSound(ModAPI.util.str("mob.duck.step"), 0.2, 1); } nme_EntityDuck.prototype.$getDropItem = function () { @@ -75,7 +75,7 @@ } nme_EntityDuck.prototype.$createChild = function (otherParent) { this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); - return new nme_EntityDuck(this.wrapped.worldObj); + return new nme_EntityDuck(this.wrapped.worldObj?.getRef() ?? null); } nme_EntityDuck.prototype.$isBreedingItem = function (itemstack) { return itemstack !== null && itemstack.$getItem() === ModAPI.items.bread.getRef(); From e38fd7c11184de8e9533dc555852c9f6366d7039 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Fri, 31 Jan 2025 17:11:20 +0800 Subject: [PATCH 36/41] force prototype-stacked methods on supertypes --- postinit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postinit.js b/postinit.js index 1c9ac14..1b5af72 100644 --- a/postinit.js +++ b/postinit.js @@ -396,7 +396,7 @@ globalThis.modapi_postinit = "(" + (() => { //Prototype Injection, allows for far easier access to methods if (typeof item === "function" && ModAPI.hooks._rippedMethodTypeMap[method] === "instance") { - item.prototype["$" + method.replace(compiledName + "_", "")] ||= function (...args) { + item.prototype["$" + method.replace(compiledName + "_", "")] = function (...args) { return ModAPI.hooks.methods[method].apply(this, [this, ...args]); } } From 91e6ca6d8c39e8e7e09272f5e32ce0b2020fd8e7 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Fri, 31 Jan 2025 17:33:26 +0800 Subject: [PATCH 37/41] fix prototype injection again --- postinit.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/postinit.js b/postinit.js index 1b5af72..53b8868 100644 --- a/postinit.js +++ b/postinit.js @@ -396,9 +396,14 @@ globalThis.modapi_postinit = "(" + (() => { //Prototype Injection, allows for far easier access to methods if (typeof item === "function" && ModAPI.hooks._rippedMethodTypeMap[method] === "instance") { - item.prototype["$" + method.replace(compiledName + "_", "")] = function (...args) { + var prototypeInjectedMethod = function prototypeInjectedMethod(...args) { return ModAPI.hooks.methods[method].apply(this, [this, ...args]); } + if ((item.prototype["$" + method.replace(compiledName + "_", "")]?.name ?? "prototypeInjectedMethod") === "prototypeInjectedMethod") { + item.prototype["$" + method.replace(compiledName + "_", "")] = prototypeInjectedMethod; + } else { + item.prototype["$" + method.replace(compiledName + "_", "")] ||= prototypeInjectedMethod; + } } } }); From 41886c52c5a0bca948952bb4921a3534f1fdc486 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Fri, 31 Jan 2025 17:34:48 +0800 Subject: [PATCH 38/41] add swimming to ducks --- examplemods/DuckMod.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/examplemods/DuckMod.js b/examplemods/DuckMod.js index c1f2592..b8a5adb 100644 --- a/examplemods/DuckMod.js +++ b/examplemods/DuckMod.js @@ -21,7 +21,6 @@ var nme_EntityDuck = function nme_EntityDuck($worldIn) { entitySuper(this, $worldIn); this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); - console.log(this.$setSize); this.wrapped.setSize(0.4, 0.7); this.wrapped.tasks.addTask(0, AITask("EntityAISwimming", 1)(this)); this.wrapped.tasks.addTask(1, AITask("EntityAIPanic", 2)(this, 1.9)); @@ -50,11 +49,11 @@ nme_EntityDuck.prototype.$onLivingUpdate = function () { this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); originalLivingUpdate.apply(this, []); - // if (this.$isInWater()) { - // this.$getEntityAttribute(SharedMonsterAttributes.movementSpeed).$setBaseValue(0.6); - // } else { - // this.$getEntityAttribute(SharedMonsterAttributes.movementSpeed).$setBaseValue(0.25); - // } + if (this.wrapped.isInWater()) { + this.wrapped.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(1.15); + } else { + this.wrapped.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.25); + } } nme_EntityDuck.prototype.$getLivingSound = function () { @@ -90,6 +89,14 @@ modelChickenSuper(this); } ModAPI.reflect.prototypeStack(modelChickenClass, nmcm_ModelDuck); + var parentSetRotationAndAngles = nmcm_ModelDuck.$setRotationAngles; + nmcm_ModelDuck.$setRotationAngles = function (f, f1, f2, f3, f4, var6, entity) { + parentSetRotationAndAngles.apply(this, [f, f1, f2, f3, f4, var6, entity]); + var wrapped = ModAPI.util.wrap(this).getCorrective(); + var wingPos = 0; + wrapped.rightWing.rotateAngleZ = wingPos; + wrapped.leftWing.rotateAngleZ = -wingPos; + } // END CUSTOM MODEL From b60476f18f2cf84b33e3a1d1f546b791068f0922 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 2 Feb 2025 15:06:16 +0800 Subject: [PATCH 39/41] add entity spawning --- examplemods/DuckMod.js | 54 ++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/examplemods/DuckMod.js b/examplemods/DuckMod.js index b8a5adb..a752d51 100644 --- a/examplemods/DuckMod.js +++ b/examplemods/DuckMod.js @@ -29,7 +29,7 @@ this.wrapped.tasks.addTask(4, AITask("EntityAIFollowParent", 2)(this, 1.2)); this.wrapped.tasks.addTask(5, AITask("EntityAIWander", 2)(this, 1.1)); this.wrapped.tasks.addTask(6, AITask("EntityAIWatchClosest", 3)(this, ModAPI.util.asClass(EntityPlayer.class), 6)); - this.wrapped.tasks.addTask(7, AITask("EntityAILookIdle", 1)(this)); + this.wrapped.tasks.addTask(7, AITask("EntityAILookIdle", 1)(this)); } ModAPI.reflect.prototypeStack(entityClass, nme_EntityDuck); nme_EntityDuck.prototype.$getEyeHeight = function () { @@ -50,7 +50,8 @@ this.wrapped ||= ModAPI.util.wrap(this).getCorrective(); originalLivingUpdate.apply(this, []); if (this.wrapped.isInWater()) { - this.wrapped.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(1.15); + this.wrapped.motionY *= 0.5; + this.wrapped.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(1.4); } else { this.wrapped.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.25); } @@ -89,14 +90,6 @@ modelChickenSuper(this); } ModAPI.reflect.prototypeStack(modelChickenClass, nmcm_ModelDuck); - var parentSetRotationAndAngles = nmcm_ModelDuck.$setRotationAngles; - nmcm_ModelDuck.$setRotationAngles = function (f, f1, f2, f3, f4, var6, entity) { - parentSetRotationAndAngles.apply(this, [f, f1, f2, f3, f4, var6, entity]); - var wrapped = ModAPI.util.wrap(this).getCorrective(); - var wingPos = 0; - wrapped.rightWing.rotateAngleZ = wingPos; - wrapped.leftWing.rotateAngleZ = -wingPos; - } // END CUSTOM MODEL @@ -111,6 +104,14 @@ nmcre_RenderDuck.prototype.$getEntityTexture = function (entity) { return duckTextures; } + nmcre_RenderDuck.prototype.$handleRotationFloat = function (entity, partialTicks) { + entity = ModAPI.util.wrap(entity); + if ((!entity.onGround) && (!entity.isInWater())) { + return 2; //falling + } else { + return 0; + } + } const ID = ModAPI.keygen.entity("duck"); ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticMethods.addMapping0.method( @@ -125,8 +126,37 @@ 0x5e3e2d, //egg base 0x269166 //egg spots ); - // Note that the spawn egg for this will not work, as spawn eggs only spawn entities that are a subclass of EntityLivingBase - console.log(ID); + + const SpawnPlacementType = ModAPI.reflect.getClassById("net.minecraft.entity.EntityLiving$SpawnPlacementType").staticVariables; + const ENTITY_PLACEMENTS = ModAPI.util.wrap( + ModAPI.reflect.getClassById("net.minecraft.entity.EntitySpawnPlacementRegistry") + .staticVariables.ENTITY_PLACEMENTS + ); + ENTITY_PLACEMENTS.put(ModAPI.util.asClass(nme_EntityDuck), SpawnPlacementType.ON_GROUND); + ModAPI.addEventListener('bootstrap', ()=>{ + const SpawnListEntry = ModAPI.reflect + .getClassById("net.minecraft.world.biome.BiomeGenBase$SpawnListEntry") + .constructors.find(x => x.length === 4); + const BiomeGenSwamp = ModAPI.util.wrap( + ModAPI.reflect.getClassById("net.minecraft.world.biome.BiomeGenBase") + .staticVariables.swampland + ); + const BiomeGenRiver = ModAPI.util.wrap( + ModAPI.reflect.getClassById("net.minecraft.world.biome.BiomeGenBase") + .staticVariables.river + ); + const BiomeGenBeach = ModAPI.util.wrap( + ModAPI.reflect.getClassById("net.minecraft.world.biome.BiomeGenBase") + .staticVariables.beach + ); + const duckSpawnSwamp = SpawnListEntry(ModAPI.util.asClass(nme_EntityDuck), 22, 3, 5); + const duckSpawnRiverBed = SpawnListEntry(ModAPI.util.asClass(nme_EntityDuck), 10, 5, 9); + const duckSpawnBeach = SpawnListEntry(ModAPI.util.asClass(nme_EntityDuck), 24, 2, 3); + BiomeGenSwamp.spawnableCreatureList.add(duckSpawnSwamp); + BiomeGenRiver.spawnableCreatureList.add(duckSpawnRiverBed); + BiomeGenBeach.spawnableCreatureList.add(duckSpawnBeach); + }); + ModAPI.addEventListener("lib:asyncsink", async () => { AsyncSink.L10N.set("entity.Duck.name", "Duck"); From bbecc3b86bb91cb11fa4c237f2bec28ebaf9f80a Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 2 Feb 2025 15:13:48 +0800 Subject: [PATCH 40/41] remove debug logging --- examplemods/DuckMod.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examplemods/DuckMod.js b/examplemods/DuckMod.js index a752d51..792c023 100644 --- a/examplemods/DuckMod.js +++ b/examplemods/DuckMod.js @@ -206,5 +206,4 @@ } ]); }); - console.log(data); })(); \ No newline at end of file From 144370a5e4bcb2d9bc13e8c72d967cfe0942d039 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 2 Feb 2025 15:14:58 +0800 Subject: [PATCH 41/41] add duck emoji in duck mod title --- examplemods/DuckMod.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examplemods/DuckMod.js b/examplemods/DuckMod.js index 792c023..9f2a89a 100644 --- a/examplemods/DuckMod.js +++ b/examplemods/DuckMod.js @@ -1,5 +1,5 @@ (function DuckEntity() { - ModAPI.meta.title("Duck Mod"); + ModAPI.meta.title("Duck Mod 🦆"); ModAPI.meta.version("v1"); ModAPI.meta.description("adds ducks to the game"); ModAPI.meta.credits("By ZXMushroom63");