From 34607605a218a64cda5048d9572e1a1a554c215a Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Fri, 1 Nov 2024 15:33:14 +0800 Subject: [PATCH 01/13] Fix bad link, add function to manually regenerate bootstrap --- docs/apidoc/utils.md | 2 ++ docs/tutorials/comingsoon.md | 1 + postinit.js | 10 +++++++--- roadmap.txt | 5 ----- 4 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 docs/tutorials/comingsoon.md delete mode 100644 roadmap.txt diff --git a/docs/apidoc/utils.md b/docs/apidoc/utils.md index fa80f21..0d3a635 100644 --- a/docs/apidoc/utils.md +++ b/docs/apidoc/utils.md @@ -37,6 +37,8 @@ Methods: - `ModAPI.util.isCritical() : boolean` - Checks wether the thread is in a critical state. - When patching methods, it is good practice to allow the method to resume as usual if this is `true`, to avoid stack implosions. (yes, those are real) +- `ModAPI.util.bootstrap() : void` + - Regenerate proxies for ModAPI.items, .blocks, .materials, .enchantments - `ModAPI.util.createArray(class, jsArray) : Object[]` - Makes a java array from a class and a javascript array. - The class parameter can be retrieved via reflect: `ModAPI.reflect.getClassById("net.minecraft.util.BlockPos").class` diff --git a/docs/tutorials/comingsoon.md b/docs/tutorials/comingsoon.md new file mode 100644 index 0000000..8276d92 --- /dev/null +++ b/docs/tutorials/comingsoon.md @@ -0,0 +1 @@ +# Coming Soon \ No newline at end of file diff --git a/postinit.js b/postinit.js index 1ad0805..640e3be 100644 --- a/postinit.js +++ b/postinit.js @@ -843,13 +843,17 @@ globalThis.modapi_postinit = "(" + (() => { return x; } - const originalBootstrap = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.init.Bootstrap", "register")]; - ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.init.Bootstrap", "register")] = function (...args) { - var x = originalBootstrap.apply(this, args); + ModAPI.util.bootstrap = function () { ModAPI.items = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.init.Items")].staticVariables, StaticProps_ProxyConf); ModAPI.blocks = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.init.Blocks")].staticVariables, StaticProps_ProxyConf); ModAPI.materials = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.block.material.Material")].staticVariables, StaticProps_ProxyConf); ModAPI.enchantments = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.enchantment.Enchantment")].staticVariables, StaticProps_ProxyConf); + } + + const originalBootstrap = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.init.Bootstrap", "register")]; + ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.init.Bootstrap", "register")] = function (...args) { + var x = originalBootstrap.apply(this, args); + ModAPI.util.bootstrap(); console.log("[ModAPI] Hooked into bootstrap. .blocks, .items, .materials and .enchantments are now accessible."); return x; } diff --git a/roadmap.txt b/roadmap.txt deleted file mode 100644 index 3685ae1..0000000 --- a/roadmap.txt +++ /dev/null @@ -1,5 +0,0 @@ -*/*/ ZXMushroom63's rather large to do list */*/ - -Add makeItemStack to LCI [Todo] -Fix blocklook.js [In progress] -Fix setblocktest.js [In progress] \ No newline at end of file From 8743dd983f89c63664ee820f2ee4815d000335eb Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sat, 2 Nov 2024 13:04:55 +0800 Subject: [PATCH 02/13] Add resourcepack support to asyncsink --- examplemods/AsyncSink.js | 95 +++++++++++++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index 8034f94..100b792 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -1,10 +1,11 @@ ModAPI.meta.title("AsyncSink"); ModAPI.meta.description("Library for patching and hooking into asynchronous filesystem requests for EaglercraftX."); -ModAPI.meta.icon("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAL9JREFUOE9jZGBg+M9ABcAIMsgtPo3hzZ2zYONEVIxJZu9aOIsBbJCRtTHcEJAgLgBSh82ic0fPIgyCKQAJXrx4EcUsfX19sBiIRrYU5gu4Qchew2cQyHSQYehBgdNruFwEcybMZci+gIcRIa+hhxu6LzBiDZvX0A1BDyuivYbLIJK8pqevjze5GlsbMxAdayCT/PQwDRS2gaQror2m36KH4SqjZybwxEl0gsQWRkM01ogpVQh6jaJihBgXEFIDAAIQ9AFDJlrxAAAAAElFTkSuQmCC"); +const asyncSinkIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAL9JREFUOE9jZGBg+M9ABcAIMsgtPo3hzZ2zYONEVIxJZu9aOIsBbJCRtTHcEJAgLgBSh82ic0fPIgyCKQAJXrx4EcUsfX19sBiIRrYU5gu4Qchew2cQyHSQYehBgdNruFwEcybMZci+gIcRIa+hhxu6LzBiDZvX0A1BDyuivYbLIJK8pqevjze5GlsbMxAdayCT/PQwDRS2gaQror2m36KH4SqjZybwxEl0gsQWRkM01ogpVQh6jaJihBgXEFIDAAIQ9AFDJlrxAAAAAElFTkSuQmCC"; +ModAPI.meta.icon(asyncSinkIcon); ModAPI.meta.credits("By ZXMushroom63"); (function AsyncSinkFn() { //AsyncSink is a plugin to debug and override asynchronous methods in EaglercraftX - function runtimeComponent() { + async function runtimeComponent() { const booleanResult = (b) => ModAPI.hooks.methods.nlevit_BooleanResult__new(b * 1); const wrap = ModAPI.hooks.methods.otji_JSWrapper_wrap; const unwrap = ModAPI.hooks.methods.otji_JSWrapper_unwrap; @@ -80,8 +81,8 @@ ModAPI.meta.credits("By ZXMushroom63"); } return wrap(AsyncSink.getFile(ModAPI.util.ustr(args[1]))); } - var ev = {method: "read", file: ModAPI.util.ustr(args[1]), shim: false, shimOutput: new ArrayBuffer()}; - AsyncSink.MIDDLEWARE.forEach((fn)=>{fn(ev)}); + var ev = { method: "read", file: ModAPI.util.ustr(args[1]), shim: false, shimOutput: new ArrayBuffer() }; + AsyncSink.MIDDLEWARE.forEach((fn) => { fn(ev) }); if (ev.shim) { return wrap(ev.shimOutput); } @@ -100,8 +101,8 @@ ModAPI.meta.credits("By ZXMushroom63"); AsyncSink.setFile(ModAPI.util.ustr(args[1]), args[2]); return booleanResult(true); } - var ev = {method: "write", file: ModAPI.util.ustr(args[1]), data: args[2], shim: false, shimOutput: true}; - AsyncSink.MIDDLEWARE.forEach((fn)=>{fn(ev)}); + var ev = { method: "write", file: ModAPI.util.ustr(args[1]), data: args[2], shim: false, shimOutput: true }; + AsyncSink.MIDDLEWARE.forEach((fn) => { fn(ev) }); if (ev.shim) { return booleanResult(ev.shimOutput); } @@ -120,8 +121,8 @@ ModAPI.meta.credits("By ZXMushroom63"); AsyncSink.deleteFile(ModAPI.util.ustr(args[1])); return booleanResult(true); } - var ev = {method: "delete", file: ModAPI.util.ustr(args[1]), shim: false, shimOutput: true}; - AsyncSink.MIDDLEWARE.forEach((fn)=>{fn(ev)}); + var ev = { method: "delete", file: ModAPI.util.ustr(args[1]), shim: false, shimOutput: true }; + AsyncSink.MIDDLEWARE.forEach((fn) => { fn(ev) }); if (ev.shim) { return booleanResult(ev.shimOutput); } @@ -140,17 +141,91 @@ ModAPI.meta.credits("By ZXMushroom63"); var result = AsyncSink.fileExists(ModAPI.util.ustr(args[1])); return booleanResult(result); } - var ev = {method: "exists", file: ModAPI.util.ustr(args[1]), shim: false, shimOutput: true}; - AsyncSink.MIDDLEWARE.forEach((fn)=>{fn(ev)}); + var ev = { method: "exists", file: ModAPI.util.ustr(args[1]), shim: false, shimOutput: true }; + AsyncSink.MIDDLEWARE.forEach((fn) => { fn(ev) }); if (ev.shim) { return booleanResult(ev.shimOutput); } return originalFileExists.apply(this, args); }; + globalThis.AsyncSink = AsyncSink; ModAPI.events.callEvent("lib:asyncsink", {}); console.log("[AsyncSink] Loaded!"); } runtimeComponent(); ModAPI.dedicatedServer.appendCode(runtimeComponent); + + + async function assureAsyncSinkResources() { + const dec = new TextDecoder("utf-8"); + const enc = new TextEncoder("utf-8"); + var resourcePackKey = (await indexedDB.databases()).find(x => x?.name?.endsWith("_resourcePacks")).name; + const dbRequest = indexedDB.open(resourcePackKey); + const db = await promisifyIDBRequest(dbRequest); + const transaction = db.transaction(["filesystem"], "readonly"); + const objectStore = transaction.objectStore("filesystem"); + var object = (await promisifyIDBRequest(objectStore.get(["resourcepacks/manifest.json"])))?.data; + var resourcePackList = object ? JSON.parse(dec.decode(object)) : { resourcePacks: [] }; + if (!resourcePackList.resourcePacks.find(x => x.name === "AsyncSinkLib")) { + resourcePackList.resourcePacks.push({ + domains: ["minecraft"], + folder: "AsyncSinkLib", + name: "AsyncSinkLib", + timestamp: Date.now() + }); + const writeableTransaction = db.transaction(["filesystem"], "readwrite"); + const writeableObjectStore = writeableTransaction.objectStore("filesystem"); + await promisifyIDBRequest(writeableObjectStore.put({ + path: "resourcepacks/manifest.json", + data: enc.encode(JSON.stringify(resourcePackList)).buffer + })); + await promisifyIDBRequest(writeableObjectStore.put({ + path: "resourcepacks/AsyncSinkLib/pack.mcmeta", + data: enc.encode(JSON.stringify({ + "pack": { + "pack_format": 1, + "description": "AsyncSink Library Resources" + } + })).buffer + })); + + var icon = { + path: "resourcepacks/AsyncSinkLib/pack.png", + data: await (await fetch(asyncSinkIcon)).arrayBuffer() + }; + + const imageTransaction = db.transaction(["filesystem"], "readwrite"); + const imageObjectStore = imageTransaction.objectStore("filesystem"); + + await promisifyIDBRequest(imageObjectStore.put(icon)); + } + } + + // Client side reminders to enable the AsyncSink Resource Pack + var asyncSinkInstallStatus = false; + var installMessage = document.createElement("span"); + installMessage.innerText = "Please enable the AsyncSink resource pack\nIn game, use the .reload_tex command to load textures for modded blocks and items."; + installMessage.style = "background-color: rgba(0,0,0,0.7); color: red; position: fixed; top: 0; left: 0; font-family: sans-serif; pointer-events: none; user-select: none;"; + document.body.appendChild(installMessage); + + assureAsyncSinkResources(); + setInterval(() => { + var resourcePackEntries = ModAPI.mc.mcResourcePackRepository.getRepositoryEntries().getCorrective(); + var array = resourcePackEntries.array || [resourcePackEntries.element]; + asyncSinkInstallStatus = array.find(x => ModAPI.util.ustr(x.reResourcePack.resourcePackFile.getRef()) === "AsyncSinkLib") ? true : false; + assureAsyncSinkResources(); + if (asyncSinkInstallStatus) { + installMessage.style.display = "none"; + } else { + installMessage.style.display = "initial"; + } + }, 8000); + + ModAPI.addEventListener("sendchatmessage", (e) => { + if (e.message.toLowerCase().startsWith(".reload_tex")) { + e.preventDefault = true; + ModAPI.promisify(ModAPI.mc.refreshResources)(); + } + }); })(); \ No newline at end of file From 541f9b4af37ec4e9c87f4f6113acd44f0cb9daab Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 3 Nov 2024 17:26:54 +0800 Subject: [PATCH 03/13] 2.2 beta --- docs/apidoc/events.md | 4 ++ docs/apidoc/reflect.md | 4 +- examplemods/AsyncSink.js | 22 +++++++ examplemods/block_of_steve.js | 117 ++++++++++++++++++++++++++++++++++ index.html | 1 + injector.js | 14 ++++ patches.js | 2 +- postinit.js | 44 ++++++++++++- 8 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 examplemods/block_of_steve.js diff --git a/docs/apidoc/events.md b/docs/apidoc/events.md index f06d52a..189ab5e 100644 --- a/docs/apidoc/events.md +++ b/docs/apidoc/events.md @@ -49,6 +49,10 @@ Can only be used in the context of the dedicated server. More: [DedicatedServerD - `serverstart`: - Called when the dedicated server starts. - Event object is blank. +- `bootstrap`: + - Called when the dedicated server registers blocks, items, materials, enchantments, etc. + - This is when you should register cstom blocks and items. + - Event object is blank. - `serverstop`: - Called when the dedicated server stops. - Event object is blank. diff --git a/docs/apidoc/reflect.md b/docs/apidoc/reflect.md index 76a3318..0e0c31b 100644 --- a/docs/apidoc/reflect.md +++ b/docs/apidoc/reflect.md @@ -44,7 +44,9 @@ 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: String?` +- `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` diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index 100b792..b1455a1 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -40,6 +40,7 @@ ModAPI.meta.credits("By ZXMushroom63"); // @type Map AsyncSink.FS = new Map(); + AsyncSink.L10N = new Map(); AsyncSink.FSOverride = new Set(); AsyncSink.MIDDLEWARE = []; AsyncSink.setFile = function setFile(path, data) { @@ -149,7 +150,27 @@ ModAPI.meta.credits("By ZXMushroom63"); return originalFileExists.apply(this, args); }; + const L10NRead = ModAPI.util.getMethodFromPackage("net.minecraft.util.StatCollector", "translateToLocal"); + const originalL10NRead = ModAPI.hooks.methods[L10NRead]; + ModAPI.hooks.methods[L10NRead] = function (...args) { + var key = ModAPI.util.ustr(args[0]); + if (AsyncSink.L10N.has(key)) { + return ModAPI.util.str(AsyncSink.L10N.get(key)); + } + return originalL10NRead.apply(this, args); + }; + + const L10NCheck = ModAPI.util.getMethodFromPackage("net.minecraft.util.StatCollector", "canTranslate"); + const originalL10NCheck = ModAPI.hooks.methods[L10NRead]; + ModAPI.hooks.methods[L10NCheck] = function (...args) { + if (AsyncSink.L10N.has(ModAPI.util.ustr(args[0]))) { + return 1; + } + return originalL10NCheck.apply(this, args); + }; + globalThis.AsyncSink = AsyncSink; + ModAPI.events.newEvent("lib:asyncsink"); ModAPI.events.callEvent("lib:asyncsink", {}); console.log("[AsyncSink] Loaded!"); } @@ -225,6 +246,7 @@ ModAPI.meta.credits("By ZXMushroom63"); ModAPI.addEventListener("sendchatmessage", (e) => { if (e.message.toLowerCase().startsWith(".reload_tex")) { e.preventDefault = true; + ModAPI.mc.renderItem.itemModelMesher.simpleShapesCache.clear(); ModAPI.promisify(ModAPI.mc.refreshResources)(); } }); diff --git a/examplemods/block_of_steve.js b/examplemods/block_of_steve.js new file mode 100644 index 0000000..d2849af --- /dev/null +++ b/examplemods/block_of_steve.js @@ -0,0 +1,117 @@ +//nice little utility function to fix the block identity map +function fixupBlockIds() { + var blockRegistry = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry).getCorrective(); + var BLOCK_STATE_IDS = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.BLOCK_STATE_IDS).getCorrective(); + blockRegistry.registryObjects.hashTableKToV.forEach(entry => { + if (entry) { + var block = entry.value; + var validStates = block.getBlockState().getValidStates(); + var stateArray = validStates.array || [validStates.element]; + stateArray.forEach(iblockstate => { + var i = blockRegistry.getIDForObject(block.getRef()) << 4 | block.getMetaFromState(iblockstate.getRef()); + BLOCK_STATE_IDS.put(iblockstate.getRef(), i); + }); + } + }); +} +function makeSteveBlock() { + var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block"); + var iproperty = ModAPI.reflect.getClassById("net.minecraft.block.properties.IProperty").class; + var makeBlockState = ModAPI.reflect.getClassById("net.minecraft.block.state.BlockState").constructors.find(x => x.length === 2); + var blockSuper = ModAPI.reflect.getSuper(blockClass, (x) => x.length === 2); + var nmb_BlockSteve = function nmb_BlockSteve() { + blockSuper(this, ModAPI.materials.rock.getRef()); + } + ModAPI.reflect.prototypeStack(blockClass, nmb_BlockSteve); + nmb_BlockSteve.prototype.$isOpaqueCube = function () { + return 1; + } + nmb_BlockSteve.prototype.$createBlockState = function () { + return makeBlockState(this, ModAPI.array.object(iproperty, 0)); + } + globalThis.nmb_BlockSteve = nmb_BlockSteve; +} +function registerSteveClientSide() { + var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item"); + var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block"); + var block_of_steve = (new nmb_BlockSteve()).$setHardness(-1.0).$setStepSound(blockClass.staticVariables.soundTypeGravel).$setUnlocalizedName( + ModAPI.util.str("steve") + ); + blockClass.staticMethods.registerBlock0.method( + 198, + ModAPI.util.str("steve"), + block_of_steve + ); + itemClass.staticMethods.registerItemBlock0.method(block_of_steve); + ModAPI.addEventListener("lib:asyncsink", async () => { + AsyncSink.L10N.set("tile.steve.name", "Block Of Steve"); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/steve.json", JSON.stringify( + { + "parent": "block/cube_all", + "textures": { + "all": "blocks/steve" + } + } + )); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/item/steve.json", JSON.stringify( + { + "parent": "block/steve", + "display": { + "thirdperson": { + "rotation": [10, -45, 170], + "translation": [0, 1.5, -2.75], + "scale": [0.375, 0.375, 0.375] + } + } + } + )); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/blockstates/steve.json", JSON.stringify( + { + "variants": { + "normal": [ + { "model": "steve" }, + ] + } + } + )); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/blocks/steve.png", await (await fetch( + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE0SURBVDhPpdO9S8NAHMbxy3sVfJmMg6h7FRXXkkUUX0addSjo4OAfIDqLIoiLi3+BRRx0EIQOnV0EcVAIWkR0KIFgrcEktX6vcXD0nuE+5Afhnhw5bWy4qylaidOfVQhT0zFKYozjBHVdzi3TwCZvteaS/0fLD8oGf5OzTeyxNUyE3Ln2HmGctpuxKuS3wd76CgPHsrEj142NeojCkHsFry+4c3aJ6g1OtlZp0Ok4DD4i+Y2GIZ+DMMAhtw+fHu8xi3IDM9t5YfMQF71dLHo+ZjsfXbh4WtnH0vYaqp/BcXGGM3D7BxiYTi+el8uYZWm2gM/VB/Tfaqje4GB5iga2Jv+sUuUa5/ITmOXq7gbnC+MY1r9QvcHG9AgN0lRex1u/ilr7ehqWvBNZvMlRbESfqNhAiG/Pb1bHXpMbFgAAAABJRU5ErkJggg==" + )).arrayBuffer()); + }); +} +function registerSteveServerSide() { + function fixupBlockIds() { + var blockRegistry = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry).getCorrective(); + var BLOCK_STATE_IDS = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.BLOCK_STATE_IDS).getCorrective(); + blockRegistry.registryObjects.hashTableKToV.forEach(entry => { + if (entry) { + var block = entry.value; + var validStates = block.getBlockState().getValidStates(); + var stateArray = validStates.array || [validStates.element]; + stateArray.forEach(iblockstate => { + var i = blockRegistry.getIDForObject(block.getRef()) << 4 | block.getMetaFromState(iblockstate.getRef()); + BLOCK_STATE_IDS.put(iblockstate.getRef(), i); + }); + } + }); + } + var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block"); + var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item"); + ModAPI.addEventListener("bootstrap", () => { + var block_of_steve = (new nmb_BlockSteve()).$setHardness(-1.0).$setStepSound(blockClass.staticVariables.soundTypeGravel).$setUnlocalizedName( + ModAPI.util.str("steve") + ); + blockClass.staticMethods.registerBlock0.method( + 198, + ModAPI.util.str("steve"), + block_of_steve + ); + itemClass.staticMethods.registerItemBlock0.method(block_of_steve); + fixupBlockIds(); + }); +} +ModAPI.dedicatedServer.appendCode(makeSteveBlock); +makeSteveBlock(); +registerSteveClientSide(); +fixupBlockIds(); +ModAPI.dedicatedServer.appendCode(registerSteveServerSide); \ No newline at end of file diff --git a/index.html b/index.html index 27d2158..4eb6424 100644 --- a/index.html +++ b/index.html @@ -167,6 +167,7 @@
ModAPI.hooks._rippedData ||= []; ModAPI.hooks._teavm ||= {}; ModAPI.hooks._rippedConstructors ||= {}; + ModAPI.hooks._rippedInternalConstructors ||= {}; ModAPI.hooks.methods ||= {}; ModAPI.hooks._rippedMethodTypeMap ||= {}; ModAPI.hooks._postInit ||= ()=>{}; diff --git a/injector.js b/injector.js index 88877ef..96bfd39 100644 --- a/injector.js +++ b/injector.js @@ -136,6 +136,20 @@ var main;(function(){` ); } ); + + const extractInternalConstructorRegex = + /^\s*function (\S*?)__init_\d*?\(\$this/gm; //same as extract constructor regex, but only allow $this as first argument + patchedFile = patchedFile.replaceAll( + extractInternalConstructorRegex, + (match) => { + var fullName = match.match(extractConstructorFullNameRegex); + fullName = fullName[0].replace("function ", ""); + return ( + `ModAPI.hooks._rippedInternalConstructors[\`${fullName}\`] = ${fullName}; +` + match + ); + } + ); if(globalThis.optimizePi){ patchedFile = patchedFile.replaceAll( diff --git a/patches.js b/patches.js index 06ee7c6..8087300 100644 --- a/patches.js +++ b/patches.js @@ -35,4 +35,4 @@ PatchesRegistry.addPatch(function (input) { if (!$this.$renderHand)` ); return output; -}) +}); \ No newline at end of file diff --git a/postinit.js b/postinit.js index 640e3be..47f7e72 100644 --- a/postinit.js +++ b/postinit.js @@ -149,7 +149,7 @@ globalThis.modapi_postinit = "(" + (() => { } if (xOut && typeof xOut === "object" && !Array.isArray(xOut)) { if (corrective) { - return new Proxy(outputValue.data, CorrectiveRecursive); + return new Proxy(xOut, CorrectiveRecursive); } return new Proxy(xOut, ModAPI.util.TeaVM_to_Recursive_BaseData_ProxyConf); } @@ -209,6 +209,7 @@ globalThis.modapi_postinit = "(" + (() => { ModAPI.hooks.regenerateClassMap = function () { ModAPI.hooks._rippedConstructorKeys = Object.keys(ModAPI.hooks._rippedConstructors); + ModAPI.hooks._rippedInternalConstructorKeys = Object.keys(ModAPI.hooks._rippedInternalConstructors); ModAPI.hooks._rippedMethodKeys = Object.keys(ModAPI.hooks._rippedMethodTypeMap); var compiledNames = new Set(); @@ -255,6 +256,7 @@ globalThis.modapi_postinit = "(" + (() => { "id": classId, "binaryName": item?.$meta?.binaryName || null, "constructors": [], + "internalConstructors": [], "methods": {}, "staticMethods": {}, "staticVariables": {}, @@ -268,9 +270,11 @@ globalThis.modapi_postinit = "(" + (() => { } } if (typeof item?.$meta?.superclass === "function" && item?.$meta?.superclass?.$meta) { - ModAPI.hooks._classMap[compiledName].superclass = item.$meta.superclass.$meta.name; + ModAPI.hooks._classMap[compiledName].superclassName = item.$meta.superclass.$meta.name; + ModAPI.hooks._classMap[compiledName].superclass = item.$meta.superclass; } else { ModAPI.hooks._classMap[compiledName].superclass = null; + ModAPI.hooks._classMap[compiledName].superclassName = null; } ModAPI.hooks._classMap[compiledName].staticVariableNames = ModAPI.hooks._rippedStaticIndexer[compiledName]; ModAPI.hooks._classMap[compiledName].staticVariables = ModAPI.hooks._rippedStaticProperties[compiledName]; @@ -286,6 +290,13 @@ globalThis.modapi_postinit = "(" + (() => { } }); } + + ModAPI.hooks._rippedInternalConstructorKeys.forEach(initialiser => { // Find internal constructors/initialisers. Used for calling super() on custom classes. (They are the different implementations of a classes constructor, that don't automatically create an object. Thus, it is identical to calling super) + if (initialiser.startsWith(compiledName + "__init_") && !initialiser.includes("$lambda$")) { + ModAPI.hooks._classMap[compiledName].internalConstructors.push(ModAPI.hooks._rippedInternalConstructors[initialiser]); + } + }); + ModAPI.hooks._rippedMethodKeys.forEach((method) => { if (method.startsWith(compiledName + "_") && !method.includes("$lambda$")) { var targetMethodMap = ModAPI.hooks._classMap[compiledName].methods; @@ -319,6 +330,31 @@ globalThis.modapi_postinit = "(" + (() => { var key = classKeys.filter(k => { return ModAPI.hooks._classMap[k].name === className })[0]; return key ? ModAPI.hooks._classMap[key] : null; } + + //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; + var initialiser = reflectClass.internalConstructors.find(filter); + return function superFunction(thisArg, ...extra_args) { + reflectClass.class.call(thisArg); + initialiser(thisArg, ...extra_args); + } + } + + //Iteratively load the superclasses' prototype methods. + ModAPI.reflect.prototypeStack = function prototypeStack(reflectClass, classFn) { + var stack = [reflectClass.class.prototype]; + var currentSuperclass = reflectClass.superclass; + while (currentSuperclass) { + stack.push(currentSuperclass.prototype); + currentSuperclass = currentSuperclass?.$meta?.superclass; + } + stack.reverse(); + stack.forEach(proto => { + Object.assign(classFn.prototype, proto); + }); + } + var reloadDeprecationWarnings = 0; const TeaVMArray_To_Recursive_BaseData_ProxyConf = { get(target, prop, receiver) { @@ -556,7 +592,7 @@ globalThis.modapi_postinit = "(" + (() => { //Function used for running @Async / @Async-dependent TeaVM methods. ModAPI.promisify = function promisify(fn) { - return function promisifiedJavaMethpd(...inArguments) { + return function promisifiedJavaMethod(...inArguments) { return new Promise((res, rej) => { Promise.resolve().then( //queue microtask () => { @@ -850,10 +886,12 @@ globalThis.modapi_postinit = "(" + (() => { ModAPI.enchantments = new Proxy(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.enchantment.Enchantment")].staticVariables, StaticProps_ProxyConf); } + ModAPI.events.newEvent("bootstrap", "server"); const originalBootstrap = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.init.Bootstrap", "register")]; ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.init.Bootstrap", "register")] = function (...args) { var x = originalBootstrap.apply(this, args); ModAPI.util.bootstrap(); + ModAPI.events.callEvent("bootstrap", {}); console.log("[ModAPI] Hooked into bootstrap. .blocks, .items, .materials and .enchantments are now accessible."); return x; } From a68c05e4a4b81cbe21ed1a3a8d47b483b80753c9 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 3 Nov 2024 18:51:53 +0800 Subject: [PATCH 04/13] make simple and advanced versions --- ...of_steve.js => block_of_steve_advanced.js} | 14 ++- examplemods/block_of_steve_simple.js | 102 ++++++++++++++++++ examplemods/dupe_hunting.js | 2 +- 3 files changed, 116 insertions(+), 2 deletions(-) rename examplemods/{block_of_steve.js => block_of_steve_advanced.js} (86%) create mode 100644 examplemods/block_of_steve_simple.js diff --git a/examplemods/block_of_steve.js b/examplemods/block_of_steve_advanced.js similarity index 86% rename from examplemods/block_of_steve.js rename to examplemods/block_of_steve_advanced.js index d2849af..561bf9e 100644 --- a/examplemods/block_of_steve.js +++ b/examplemods/block_of_steve_advanced.js @@ -15,23 +15,35 @@ function fixupBlockIds() { }); } function makeSteveBlock() { + var boolean_valueOf = ModAPI.reflect.getClassByName("Boolean").staticMethods.valueOf.method; + var testProperty = ModAPI.reflect.getClassById("net.minecraft.block.properties.PropertyBool").staticMethods.create.method(ModAPI.util.str("example_property")); var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block"); var iproperty = ModAPI.reflect.getClassById("net.minecraft.block.properties.IProperty").class; var makeBlockState = ModAPI.reflect.getClassById("net.minecraft.block.state.BlockState").constructors.find(x => x.length === 2); var blockSuper = ModAPI.reflect.getSuper(blockClass, (x) => x.length === 2); + var creativeBlockTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabBlock; var nmb_BlockSteve = function nmb_BlockSteve() { blockSuper(this, ModAPI.materials.rock.getRef()); + this.$defaultBlockState = this.$blockState.$getBaseState(); + this.$setCreativeTab(creativeBlockTab); } ModAPI.reflect.prototypeStack(blockClass, nmb_BlockSteve); nmb_BlockSteve.prototype.$isOpaqueCube = function () { return 1; } - nmb_BlockSteve.prototype.$createBlockState = function () { + nmb_BlockSteve.prototype.$createBlockState = function (t) { return makeBlockState(this, ModAPI.array.object(iproperty, 0)); } + // nmb_BlockSteve.prototype.$getMetaFromState = function (iblockstate) { + // return iblockstate.$getValue(testProperty).$booleanValue(); + // } + // nmb_BlockSteve.prototype.$getStateFromMeta = function (meta) { + // return this.$getDefaultState().$withProperty(testProperty, boolean_valueOf(meta > 0)) + // } globalThis.nmb_BlockSteve = nmb_BlockSteve; } function registerSteveClientSide() { + globalThis.testBlockImpl = ModAPI.util.wrap(new nmb_BlockSteve()); var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item"); var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block"); var block_of_steve = (new nmb_BlockSteve()).$setHardness(-1.0).$setStepSound(blockClass.staticVariables.soundTypeGravel).$setUnlocalizedName( diff --git a/examplemods/block_of_steve_simple.js b/examplemods/block_of_steve_simple.js new file mode 100644 index 0000000..4af0073 --- /dev/null +++ b/examplemods/block_of_steve_simple.js @@ -0,0 +1,102 @@ +//nice little utility function to fix the block identity map +function fixupBlockIds() { + var blockRegistry = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry).getCorrective(); + var BLOCK_STATE_IDS = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.BLOCK_STATE_IDS).getCorrective(); + blockRegistry.registryObjects.hashTableKToV.forEach(entry => { + if (entry) { + var block = entry.value; + var validStates = block.getBlockState().getValidStates(); + var stateArray = validStates.array || [validStates.element]; + stateArray.forEach(iblockstate => { + var i = blockRegistry.getIDForObject(block.getRef()) << 4 | block.getMetaFromState(iblockstate.getRef()); + BLOCK_STATE_IDS.put(iblockstate.getRef(), i); + }); + } + }); +} +function registerSteveClientSide() { + var creativeBlockTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabBlock; + var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item"); + var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block"); + var constructor = blockClass.constructors.find(x=>x.length === 1); + var block_of_steve = constructor(ModAPI.materials.rock.getRef()).$setHardness(-1.0).$setStepSound(blockClass.staticVariables.soundTypeGravel).$setUnlocalizedName( + ModAPI.util.str("steve") + ).$setCreativeTab(creativeBlockTab); + blockClass.staticMethods.registerBlock0.method( + 198, + ModAPI.util.str("steve"), + block_of_steve + ); + itemClass.staticMethods.registerItemBlock0.method(block_of_steve); + ModAPI.addEventListener("lib:asyncsink", async () => { + AsyncSink.L10N.set("tile.steve.name", "Block Of Steve"); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/steve.json", JSON.stringify( + { + "parent": "block/cube_all", + "textures": { + "all": "blocks/steve" + } + } + )); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/item/steve.json", JSON.stringify( + { + "parent": "block/steve", + "display": { + "thirdperson": { + "rotation": [10, -45, 170], + "translation": [0, 1.5, -2.75], + "scale": [0.375, 0.375, 0.375] + } + } + } + )); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/blockstates/steve.json", JSON.stringify( + { + "variants": { + "normal": [ + { "model": "steve" }, + ] + } + } + )); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/blocks/steve.png", await (await fetch( + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE0SURBVDhPpdO9S8NAHMbxy3sVfJmMg6h7FRXXkkUUX0addSjo4OAfIDqLIoiLi3+BRRx0EIQOnV0EcVAIWkR0KIFgrcEktX6vcXD0nuE+5Afhnhw5bWy4qylaidOfVQhT0zFKYozjBHVdzi3TwCZvteaS/0fLD8oGf5OzTeyxNUyE3Ln2HmGctpuxKuS3wd76CgPHsrEj142NeojCkHsFry+4c3aJ6g1OtlZp0Ok4DD4i+Y2GIZ+DMMAhtw+fHu8xi3IDM9t5YfMQF71dLHo+ZjsfXbh4WtnH0vYaqp/BcXGGM3D7BxiYTi+el8uYZWm2gM/VB/Tfaqje4GB5iga2Jv+sUuUa5/ITmOXq7gbnC+MY1r9QvcHG9AgN0lRex1u/ilr7ehqWvBNZvMlRbESfqNhAiG/Pb1bHXpMbFgAAAABJRU5ErkJggg==" + )).arrayBuffer()); + }); +} +function registerSteveServerSide() { + function fixupBlockIds() { + var blockRegistry = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry).getCorrective(); + var BLOCK_STATE_IDS = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.BLOCK_STATE_IDS).getCorrective(); + blockRegistry.registryObjects.hashTableKToV.forEach(entry => { + if (entry) { + var block = entry.value; + var validStates = block.getBlockState().getValidStates(); + var stateArray = validStates.array || [validStates.element]; + stateArray.forEach(iblockstate => { + var i = blockRegistry.getIDForObject(block.getRef()) << 4 | block.getMetaFromState(iblockstate.getRef()); + BLOCK_STATE_IDS.put(iblockstate.getRef(), i); + }); + } + }); + } + var creativeBlockTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabBlock; + var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block"); + var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item"); + var constructor = blockClass.constructors.find(x=>x.length === 1); + ModAPI.addEventListener("bootstrap", () => { + var block_of_steve = constructor(ModAPI.materials.rock.getRef()).$setHardness(-1.0).$setStepSound(blockClass.staticVariables.soundTypeGravel).$setUnlocalizedName( + ModAPI.util.str("steve") + ).$setCreativeTab(creativeBlockTab); + blockClass.staticMethods.registerBlock0.method( + 198, + ModAPI.util.str("steve"), + block_of_steve + ); + itemClass.staticMethods.registerItemBlock0.method(block_of_steve); + fixupBlockIds(); + }); +} +registerSteveClientSide(); +fixupBlockIds(); +ModAPI.dedicatedServer.appendCode(registerSteveServerSide); \ No newline at end of file diff --git a/examplemods/dupe_hunting.js b/examplemods/dupe_hunting.js index 74f9465..023ed5b 100644 --- a/examplemods/dupe_hunting.js +++ b/examplemods/dupe_hunting.js @@ -92,7 +92,7 @@ function button_utility_script(inputArr, bindingClass, actionBindMode) { { text: "Server Close", click: () => { - var CloseWindow = ModAPI.reflect.getClassByName("C0DPacketCloseWindow").constructors.find(x => x.length === 1); + var CloseWindow = ModAPI.reflect.getClassByName("C0DPacketCloseWindow").constructors[0]; ModAPI.player.sendQueue.addToSendQueue(CloseWindow(ModAPI.player.openContainer.getCorrective().windowId)); }, x: 0, From 78b765e5084a53a2db7a27ad3901d22958e2f215 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sun, 3 Nov 2024 19:25:57 +0800 Subject: [PATCH 05/13] push latest asyncsink and bugfixes --- examplemods/AsyncSink.js | 3 ++- examplemods/block_of_steve_advanced.js | 12 ++++-------- examplemods/block_of_steve_simple.js | 7 +++++++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index b1455a1..101b3b7 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -242,12 +242,13 @@ ModAPI.meta.credits("By ZXMushroom63"); installMessage.style.display = "initial"; } }, 8000); - + ModAPI.events.newEvent("custom:asyncsink_reloaded"); ModAPI.addEventListener("sendchatmessage", (e) => { if (e.message.toLowerCase().startsWith(".reload_tex")) { e.preventDefault = true; ModAPI.mc.renderItem.itemModelMesher.simpleShapesCache.clear(); ModAPI.promisify(ModAPI.mc.refreshResources)(); + ModAPI.events.callEvent("custom:asyncsink_reloaded", {}); } }); })(); \ No newline at end of file diff --git a/examplemods/block_of_steve_advanced.js b/examplemods/block_of_steve_advanced.js index 561bf9e..ba5d185 100644 --- a/examplemods/block_of_steve_advanced.js +++ b/examplemods/block_of_steve_advanced.js @@ -15,8 +15,6 @@ function fixupBlockIds() { }); } function makeSteveBlock() { - var boolean_valueOf = ModAPI.reflect.getClassByName("Boolean").staticMethods.valueOf.method; - var testProperty = ModAPI.reflect.getClassById("net.minecraft.block.properties.PropertyBool").staticMethods.create.method(ModAPI.util.str("example_property")); var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block"); var iproperty = ModAPI.reflect.getClassById("net.minecraft.block.properties.IProperty").class; var makeBlockState = ModAPI.reflect.getClassById("net.minecraft.block.state.BlockState").constructors.find(x => x.length === 2); @@ -34,12 +32,6 @@ function makeSteveBlock() { nmb_BlockSteve.prototype.$createBlockState = function (t) { return makeBlockState(this, ModAPI.array.object(iproperty, 0)); } - // nmb_BlockSteve.prototype.$getMetaFromState = function (iblockstate) { - // return iblockstate.$getValue(testProperty).$booleanValue(); - // } - // nmb_BlockSteve.prototype.$getStateFromMeta = function (meta) { - // return this.$getDefaultState().$withProperty(testProperty, boolean_valueOf(meta > 0)) - // } globalThis.nmb_BlockSteve = nmb_BlockSteve; } function registerSteveClientSide() { @@ -55,7 +47,11 @@ function registerSteveClientSide() { block_of_steve ); itemClass.staticMethods.registerItemBlock0.method(block_of_steve); + ModAPI.mc.renderItem.registerBlock(block_of_steve, ModAPI.util.str("steve")); ModAPI.addEventListener("lib:asyncsink", async () => { + ModAPI.addEventListener("custom:asyncsink_reloaded", ()=>{ + ModAPI.mc.renderItem.registerBlock(block_of_steve, ModAPI.util.str("steve")); + }); 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 4af0073..a6b8a7e 100644 --- a/examplemods/block_of_steve_simple.js +++ b/examplemods/block_of_steve_simple.js @@ -1,3 +1,4 @@ +AsyncSink.MIDDLEWARE.push(ev=>{if (ev.method === "read" && ev.file.includes("steve")) {console.log(ev.file)}}); //nice little utility function to fix the block identity map function fixupBlockIds() { var blockRegistry = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry).getCorrective(); @@ -28,7 +29,13 @@ function registerSteveClientSide() { block_of_steve ); itemClass.staticMethods.registerItemBlock0.method(block_of_steve); + ModAPI.mc.renderItem.registerBlock(block_of_steve, ModAPI.util.str("steve")); + + ModAPI.addEventListener("lib:asyncsink", async () => { + ModAPI.addEventListener("custom:asyncsink_reloaded", ()=>{ + ModAPI.mc.renderItem.registerBlock(block_of_steve, ModAPI.util.str("steve")); + }); AsyncSink.L10N.set("tile.steve.name", "Block Of Steve"); AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/steve.json", JSON.stringify( { From 9b3f7fc5bf4d53c3052818183f1f3b1b5d935dee Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Mon, 4 Nov 2024 16:33:58 +0800 Subject: [PATCH 06/13] bump ver, fix bug --- examplemods/block_of_steve_advanced.js | 1 - postinit.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/examplemods/block_of_steve_advanced.js b/examplemods/block_of_steve_advanced.js index ba5d185..ebbfa43 100644 --- a/examplemods/block_of_steve_advanced.js +++ b/examplemods/block_of_steve_advanced.js @@ -35,7 +35,6 @@ function makeSteveBlock() { globalThis.nmb_BlockSteve = nmb_BlockSteve; } function registerSteveClientSide() { - globalThis.testBlockImpl = ModAPI.util.wrap(new nmb_BlockSteve()); var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item"); var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block"); var block_of_steve = (new nmb_BlockSteve()).$setHardness(-1.0).$setStepSound(blockClass.staticVariables.soundTypeGravel).$setUnlocalizedName( diff --git a/postinit.js b/postinit.js index 47f7e72..064550a 100644 --- a/postinit.js +++ b/postinit.js @@ -22,7 +22,7 @@ globalThis.modapi_postinit = "(" + (() => { ModAPI.meta._versionMap = {}; ModAPI.array = {}; - ModAPI.version = "v2.1.2"; + ModAPI.version = "v2.2"; ModAPI.flavour = "injector"; ModAPI.GNU = "terry pratchett"; ModAPI.credits = ["ZXMushroom63", "radmanplays", "Murturtle", "OtterCodes101", "TheIdiotPlays", "OeildeLynx31", "Stpv22"]; From d5c8d8fdc8bc9dc2f6bd29048488ce683d19ce81 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Mon, 4 Nov 2024 16:35:57 +0800 Subject: [PATCH 07/13] fix bug --- examplemods/block_of_steve_advanced.js | 2 +- examplemods/block_of_steve_simple.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examplemods/block_of_steve_advanced.js b/examplemods/block_of_steve_advanced.js index ebbfa43..2c955b1 100644 --- a/examplemods/block_of_steve_advanced.js +++ b/examplemods/block_of_steve_advanced.js @@ -41,7 +41,7 @@ function registerSteveClientSide() { ModAPI.util.str("steve") ); blockClass.staticMethods.registerBlock0.method( - 198, + 198, //use blockid 198 ModAPI.util.str("steve"), block_of_steve ); diff --git a/examplemods/block_of_steve_simple.js b/examplemods/block_of_steve_simple.js index a6b8a7e..bd023e3 100644 --- a/examplemods/block_of_steve_simple.js +++ b/examplemods/block_of_steve_simple.js @@ -1,4 +1,3 @@ -AsyncSink.MIDDLEWARE.push(ev=>{if (ev.method === "read" && ev.file.includes("steve")) {console.log(ev.file)}}); //nice little utility function to fix the block identity map function fixupBlockIds() { var blockRegistry = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry).getCorrective(); @@ -24,7 +23,7 @@ function registerSteveClientSide() { ModAPI.util.str("steve") ).$setCreativeTab(creativeBlockTab); blockClass.staticMethods.registerBlock0.method( - 198, + 198, //use blockid 198 ModAPI.util.str("steve"), block_of_steve ); From 2354c628f274ce922f93829b1ca130e0b5d82ddc Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Mon, 4 Nov 2024 16:44:41 +0800 Subject: [PATCH 08/13] add warning --- examplemods/block_of_steve_advanced.js | 3 +++ examplemods/block_of_steve_simple.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/examplemods/block_of_steve_advanced.js b/examplemods/block_of_steve_advanced.js index 2c955b1..24bc3d0 100644 --- a/examplemods/block_of_steve_advanced.js +++ b/examplemods/block_of_steve_advanced.js @@ -1,3 +1,6 @@ +//THIS IS A DEMO MOD + + //nice little utility function to fix the block identity map function fixupBlockIds() { var blockRegistry = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry).getCorrective(); diff --git a/examplemods/block_of_steve_simple.js b/examplemods/block_of_steve_simple.js index bd023e3..46b6e25 100644 --- a/examplemods/block_of_steve_simple.js +++ b/examplemods/block_of_steve_simple.js @@ -1,3 +1,6 @@ +//THIS IS A DEMO MOD + + //nice little utility function to fix the block identity map function fixupBlockIds() { var blockRegistry = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry).getCorrective(); From f0210d18ef79339d8577f612f306d07b1d9ffc42 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Tue, 5 Nov 2024 15:46:23 +0800 Subject: [PATCH 09/13] Optimise injector, fix asyncsink and UNLUCKY BLOCK MOD!!! --- examplemods/AsyncSink.js | 5 +- examplemods/unlucky_blocks.js | 134 ++++++++++++++++++++++++++++++++++ injector.js | 27 +++++-- postinit.js | 4 +- 4 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 examplemods/unlucky_blocks.js diff --git a/examplemods/AsyncSink.js b/examplemods/AsyncSink.js index 101b3b7..b5f6fd4 100644 --- a/examplemods/AsyncSink.js +++ b/examplemods/AsyncSink.js @@ -247,8 +247,9 @@ 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)(); - ModAPI.events.callEvent("custom:asyncsink_reloaded", {}); + ModAPI.promisify(ModAPI.mc.refreshResources)().then(()=>{ + ModAPI.events.callEvent("custom:asyncsink_reloaded", {}); + }); } }); })(); \ No newline at end of file diff --git a/examplemods/unlucky_blocks.js b/examplemods/unlucky_blocks.js new file mode 100644 index 0000000..0967a99 --- /dev/null +++ b/examplemods/unlucky_blocks.js @@ -0,0 +1,134 @@ +(()=>{ + const unluckyBlockTexture = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAOhJREFUOE9jnJn2/z8DGvj4koHh9UOIoKg8AwO/OLoKBJ8RZED6LEbcKvDIzEz7z8DY5f//f9lGTANCGTAcxrCaAVUd2IBSg///uy+gSqBr/sJwF+6O7QwqcHaXPxYXgDTDNCArtmXYBtZ4mMELbkCpwX8GjDDwZLjD8IXhFopCkA5s4mAXEBOIMC+BXIbsKnAYEDIAOTywBiI+A0DO5mFQBvsZXTNIjKALQAHHw6CG4mzkZIE3HYAUEjIAHIjY0gGxyRIcjfgMgIUBNv+DLAEbgCspgxTAYgCXAUSnA1xegscCPj8TytoAA5eEOx9BbCgAAAAASUVORK5CYII="; + ModAPI.meta.title("Unlucky Blocks"); + ModAPI.meta.version("v1.0"); + ModAPI.meta.description("These purple cubes ruined my life. Requires AsyncSink."); + ModAPI.meta.credits("By ZXMushroom63"); + ModAPI.meta.icon(unluckyBlockTexture); + + function UnluckyBlocks() { + function fixupBlockIds() { + var blockRegistry = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry).getCorrective(); + var BLOCK_STATE_IDS = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.BLOCK_STATE_IDS).getCorrective(); + blockRegistry.registryObjects.hashTableKToV.forEach(entry => { + if (entry) { + var block = entry.value; + var validStates = block.getBlockState().getValidStates(); + var stateArray = validStates.array || [validStates.element]; + stateArray.forEach(iblockstate => { + var i = blockRegistry.getIDForObject(block.getRef()) << 4 | block.getMetaFromState(iblockstate.getRef()); + BLOCK_STATE_IDS.put(iblockstate.getRef(), i); + }); + } + }); + } + var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item"); + var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block"); + var iproperty = ModAPI.reflect.getClassById("net.minecraft.block.properties.IProperty").class; + var makeBlockState = ModAPI.reflect.getClassById("net.minecraft.block.state.BlockState").constructors.find(x => x.length === 2); + var blockSuper = ModAPI.reflect.getSuper(blockClass, (x) => x.length === 2); + var creativeBlockTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabBlock; + var breakBlockMethod = blockClass.methods.breakBlock.method; + var nmb_BlockUnlucky = function nmb_BlockUnlucky() { + blockSuper(this, ModAPI.materials.rock.getRef()); + this.$defaultBlockState = this.$blockState.$getBaseState(); + this.$setCreativeTab(creativeBlockTab); + } + ModAPI.reflect.prototypeStack(blockClass, nmb_BlockUnlucky); + nmb_BlockUnlucky.prototype.$isOpaqueCube = function () { + return 1; + } + nmb_BlockUnlucky.prototype.$createBlockState = function () { + return makeBlockState(this, ModAPI.array.object(iproperty, 0)); + } + nmb_BlockUnlucky.prototype.$breakBlock = function ($world, $blockpos, $blockstate) { + var world = ModAPI.util.wrap($world); + var blockpos = ModAPI.util.wrap($blockpos); + if (Math.random() < 1) { //was gonna add random events but couldn't be bothered. Enjoy exploding! + world.newExplosion(null, blockpos.getX() + 0.5, blockpos.getY() + 0.5, + blockpos.getZ() + 0.5, 9, 1, 1); + } + return breakBlockMethod(this, $world, $blockpos, $blockstate); + } + + function internal_reg() { + var block_of_unluckiness = (new nmb_BlockUnlucky()).$setHardness(0.0).$setStepSound(blockClass.staticVariables.soundTypePiston).$setUnlocalizedName( + ModAPI.util.str("unluckiness") + ); + blockClass.staticMethods.registerBlock0.method( + 544, + ModAPI.util.str("unluckiness"), + block_of_unluckiness + ); + itemClass.staticMethods.registerItemBlock0.method(block_of_unluckiness); + fixupBlockIds(); + ModAPI.blocks["unluckiness"] = block_of_unluckiness; + + return block_of_unluckiness; + } + + const WorldGenMineable = ModAPI.reflect.getClassById("net.minecraft.world.gen.feature.WorldGenMinable").constructors.find(x=>x.length===2); + + const BiomeDecorator_decorate = ModAPI.util.getMethodFromPackage("net.minecraft.world.biome.BiomeDecorator", "decorate"); + const oldDecorate = ModAPI.hooks.methods[BiomeDecorator_decorate]; + ModAPI.hooks.methods[BiomeDecorator_decorate] = function ($this, $world, $random, $biomeGenBase, $blockpos) { + if (!$this.$currentWorld) { + $this.$unluckyBlockGen = WorldGenMineable(ModAPI.blocks.unluckiness.getDefaultState().getRef(), 5); + } + return oldDecorate.apply(this, [$this, $world, $random, $biomeGenBase, $blockpos]); + } + + const BiomeDecorator_generateOres = ModAPI.util.getMethodFromPackage("net.minecraft.world.biome.BiomeDecorator", "generateOres"); + const oldGenerateOres = ModAPI.hooks.methods[BiomeDecorator_generateOres]; + ModAPI.hooks.methods[BiomeDecorator_generateOres] = function ($this) { + $this.$genStandardOre1(145, $this.$unluckyBlockGen || null, 0, 256); + return oldGenerateOres.apply(this, [$this]); + } + + if (ModAPI.materials) { + return internal_reg(); + } else { + ModAPI.addEventListener("bootstrap", internal_reg); + } + } + ModAPI.dedicatedServer.appendCode(UnluckyBlocks); + var block_of_unluckiness = UnluckyBlocks(); + ModAPI.addEventListener("lib:asyncsink", async () => { + ModAPI.addEventListener("custom:asyncsink_reloaded", ()=>{ + ModAPI.mc.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( + { + "parent": "block/cube_all", + "textures": { + "all": "blocks/unluckiness" + } + } + )); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/item/unluckiness.json", JSON.stringify( + { + "parent": "block/unluckiness", + "display": { + "thirdperson": { + "rotation": [10, -45, 170], + "translation": [0, 1.5, -2.75], + "scale": [0.375, 0.375, 0.375] + } + } + } + )); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/blockstates/unluckiness.json", JSON.stringify( + { + "variants": { + "normal": [ + { "model": "unluckiness" }, + ] + } + } + )); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/blocks/unluckiness.png", await (await fetch( + unluckyBlockTexture + )).arrayBuffer()); + }); +})(); \ No newline at end of file diff --git a/injector.js b/injector.js index 96bfd39..919ae40 100644 --- a/injector.js +++ b/injector.js @@ -9,13 +9,22 @@ function _status(x) { document.querySelector("#status").innerText = x; } function entriesToStaticVariableProxy(entries, prefix) { + if (entries.length === 0) { + return `ModAPI.hooks._rippedStaticProperties[\`${prefix.replace( + "var ", + "" + )}\`]={};`; + } var getComponents = ""; entries.forEach((entry) => { getComponents += ` - case \`${entry.name}\`: - return ${entry.variable}; - break;`; + case \`${entry.name}\`: + return ${entry.variable}; + break;`; }); + getComponents += ` + default: + return Reflect.get(a,b,c);` var setComponents = ""; entries.forEach((entry) => { @@ -24,7 +33,11 @@ function entriesToStaticVariableProxy(entries, prefix) { ${entry.variable} = c; break;`; }); - var proxy = ` + setComponents += ` + default: + a[b]=c;` + /*/ + ModAPI.hooks._rippedStaticIndexer[\`${prefix.replace( "var ", "" @@ -33,7 +46,8 @@ function entriesToStaticVariableProxy(entries, prefix) { return '"' + x.name + '"'; }) .join(",")}]; - ModAPI.hooks._rippedStaticProperties[\`${prefix.replace( + /*/ + var proxy = `ModAPI.hooks._rippedStaticProperties[\`${prefix.replace( "var ", "" )}\`] = new Proxy({${entries @@ -51,6 +65,7 @@ function entriesToStaticVariableProxy(entries, prefix) { switch (b) { ${setComponents} } + return true; } });`; return proxy; @@ -238,7 +253,7 @@ var main;(function(){` patchedFile = patchedFile.replaceAll( - /function [a-z]+?_([a-zA-Z\$]+?)\(\) \{/gm, + /function [a-z]+?_([a-zA-Z0-9\$]+?)\(\) \{/gm, (match) => { var prefix = "var " + match.replace("function ", "").replace("() {", ""); var entries = []; diff --git a/postinit.js b/postinit.js index 064550a..5f52286 100644 --- a/postinit.js +++ b/postinit.js @@ -276,8 +276,10 @@ globalThis.modapi_postinit = "(" + (() => { ModAPI.hooks._classMap[compiledName].superclass = null; ModAPI.hooks._classMap[compiledName].superclassName = null; } - ModAPI.hooks._classMap[compiledName].staticVariableNames = ModAPI.hooks._rippedStaticIndexer[compiledName]; + ModAPI.hooks._classMap[compiledName].staticVariables = ModAPI.hooks._rippedStaticProperties[compiledName]; + ModAPI.hooks._classMap[compiledName].staticVariableNames = Object.keys(ModAPI.hooks._classMap[compiledName].staticVariables || {}); + if (item?.["$$constructor$$"]) { //Class does not have any hand written constructors //Eg: class MyClass {} From 3a8ac91e92feb476af8cd55dd295ac4c01012f3d Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Tue, 5 Nov 2024 17:46:17 +0800 Subject: [PATCH 10/13] fix unlucky blocks --- examplemods/unlucky_blocks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examplemods/unlucky_blocks.js b/examplemods/unlucky_blocks.js index 0967a99..72fd3a3 100644 --- a/examplemods/unlucky_blocks.js +++ b/examplemods/unlucky_blocks.js @@ -73,7 +73,7 @@ const oldDecorate = ModAPI.hooks.methods[BiomeDecorator_decorate]; ModAPI.hooks.methods[BiomeDecorator_decorate] = function ($this, $world, $random, $biomeGenBase, $blockpos) { if (!$this.$currentWorld) { - $this.$unluckyBlockGen = WorldGenMineable(ModAPI.blocks.unluckiness.getDefaultState().getRef(), 5); + $this.$unluckyBlockGen = WorldGenMineable(ModAPI.blocks.unluckiness.getDefaultState().getRef(), 4); } return oldDecorate.apply(this, [$this, $world, $random, $biomeGenBase, $blockpos]); } @@ -81,7 +81,7 @@ const BiomeDecorator_generateOres = ModAPI.util.getMethodFromPackage("net.minecraft.world.biome.BiomeDecorator", "generateOres"); const oldGenerateOres = ModAPI.hooks.methods[BiomeDecorator_generateOres]; ModAPI.hooks.methods[BiomeDecorator_generateOres] = function ($this) { - $this.$genStandardOre1(145, $this.$unluckyBlockGen || null, 0, 256); + $this.$genStandardOre1(105, $this.$unluckyBlockGen || null, 0, 256); return oldGenerateOres.apply(this, [$this]); } From 74799f265b9f55a58b329bf38ab1b44fded1909a Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Tue, 5 Nov 2024 17:51:33 +0800 Subject: [PATCH 11/13] a bit of documentation --- examplemods/unlucky_blocks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examplemods/unlucky_blocks.js b/examplemods/unlucky_blocks.js index 72fd3a3..7943d92 100644 --- a/examplemods/unlucky_blocks.js +++ b/examplemods/unlucky_blocks.js @@ -26,11 +26,11 @@ var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block"); var iproperty = ModAPI.reflect.getClassById("net.minecraft.block.properties.IProperty").class; var makeBlockState = ModAPI.reflect.getClassById("net.minecraft.block.state.BlockState").constructors.find(x => x.length === 2); - var blockSuper = ModAPI.reflect.getSuper(blockClass, (x) => x.length === 2); + var blockSuper = ModAPI.reflect.getSuper(blockClass, (x) => x.length === 2); //Get super function from the block class with a target length of two. ($this (mandatory), material (optional)) var creativeBlockTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabBlock; var breakBlockMethod = blockClass.methods.breakBlock.method; var nmb_BlockUnlucky = function nmb_BlockUnlucky() { - blockSuper(this, ModAPI.materials.rock.getRef()); + blockSuper(this, ModAPI.materials.rock.getRef()); //Use super function to get block properties on this class. this.$defaultBlockState = this.$blockState.$getBaseState(); this.$setCreativeTab(creativeBlockTab); } From 6e8598c180f96a65c0c101be72e6d0fa53195404 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Tue, 5 Nov 2024 17:51:51 +0800 Subject: [PATCH 12/13] send over. --- docs/apidoc/reflect.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/apidoc/reflect.md b/docs/apidoc/reflect.md index 0e0c31b..5e3999e 100644 --- a/docs/apidoc/reflect.md +++ b/docs/apidoc/reflect.md @@ -16,6 +16,9 @@ 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` + - Gets a super function from a reflect class. This is used to extend build in classes, like `Block`. + - For an example, see lines ### ReflectClass Definition From 85ac018b34efcd932f95562f1b29e907e19a8441 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Tue, 5 Nov 2024 17:57:50 +0800 Subject: [PATCH 13/13] add documentation --- docs/apidoc/reflect.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/apidoc/reflect.md b/docs/apidoc/reflect.md index 5e3999e..2456270 100644 --- a/docs/apidoc/reflect.md +++ b/docs/apidoc/reflect.md @@ -17,9 +17,11 @@ Methods: - 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` - - Gets a super function from a reflect class. This is used to extend build in classes, like `Block`. - - For an example, see lines - + - 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 +- `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) ### ReflectClass Definition Each `ReflectClass` has the following properties: