From dd480b15236206ba64d42d7072e503e0c2714e8e Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Thu, 5 Sep 2024 16:14:31 +0800 Subject: [PATCH] Add ModAPI.world, large progress on documentation --- docs/apidoc/hooks.md | 26 +++++++++++++++++---- docs/apidoc/index.md | 52 ++++++++++++++++++++++++++++++++++++++++- examplemods/talkback.js | 4 ++-- postinit.injector.js | 12 +++++++++- postinit.js | 12 +++++++++- 5 files changed, 97 insertions(+), 9 deletions(-) diff --git a/docs/apidoc/hooks.md b/docs/apidoc/hooks.md index 3bd910e..8614a0c 100644 --- a/docs/apidoc/hooks.md +++ b/docs/apidoc/hooks.md @@ -1,5 +1,23 @@ ## ModAPI.hooks -- To replace a function with another, you can use: - - `ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("com.package.abc.MyClass", "myMethod")] = function () {}` - - To intercept inputs to a function, you can us - - `ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("com.package.abc.MyClass", "myMethod")] = function () {}` \ No newline at end of file +ModAPI.hooks is the global generated by actual code. +It has quite a few propeties, but most of them are just foundations for more user friendly parts of the ModAPI, so I won't explain them. + +### Property: ModAPI.hooks.methods +`ModAPI.hooks.methods` is a String-to-method dictionary/object of every java method. This allows you to do pretty much whatever you want in terms of modifying and hooking into code. + +- To replace a method with another, you can use: + - `ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("com.package.abc.MyClass", "myMethod")] = function () {}` +- To intercept inputs to a method, you can us + - ```javascript + var myMethodName = ModAPI.util.getMethodFromPackage("com.package.abc.MyClass", "myMethod"); + const originalMethod = ModAPI.hooks.methods[myMethodName]; + ModAPI.hooks.methods[myMethodName] = function (...args) { + //args is the array of arguments passed in. + //on an instance method, this first (args[0]) will almost always + //be $this, ie: the object the method is being run on + return originalMethod.apply(this, args); + } + ``` + +### Property: ModAPI.hooks._teavm +`ModAPI.hooks._teavm` is usually only used for internal purposes, but it is basically a collection of every internal TeaVM method. Keep in mind that this only stores references (for performance reasons), so modifying and editing it's contents will not affect the way the game runs. \ No newline at end of file diff --git a/docs/apidoc/index.md b/docs/apidoc/index.md index bd08428..ac43769 100644 --- a/docs/apidoc/index.md +++ b/docs/apidoc/index.md @@ -4,6 +4,8 @@ The EaglerForge ModAPI is housed in a global JavaScript object stored on `global The global object has the following properties: - `ModAPI.player: EntityPlayerSP` - Only accessible after `ModAPI.require("player")` is called, this is the local player entity. It is regenerated every time the `update` event is called. +- `ModAPI.world: WorldClient` + - Only accessible after `ModAPI.require("world")` is called, this is the client-side world. It is regenerated every time the `update` event is called. - `ModAPI.network: NetHandlerPlayClient` - Only accessible after `ModAPI.require("network")` is called, this is the client's networking handler. It is regenerated every time the `update` event is called. - `ModAPI.settings: GameSettings` @@ -68,4 +70,52 @@ The ModAPI object has the following methods: - Usage: `ModAPI.clickMouse()` - `rightClickMouse() : void` - Triggers a right click ingame. - - Usage: `ModAPI.rightClickMouse()` \ No newline at end of file + - Usage: `ModAPI.rightClickMouse()` + + +## Handling strings, numbers and booleans to and from java. +Java strings and JavaScript strings are not the same. Calling a method like this: `ModAPI.player.sendChatMessage("hello world")`, will not work, as you are running a Java method with a JavaScript string. To convert a JS string to a Java string, use `ModAPI.util.str(yourString)`. For example, the correct version of the above example is `ModAPI.player.sendChatMessage(ModAPI.util.str("hello world"))`. This problem is automatically mitigated on a few functions, namely `ModAPI.displayToChat()`. + + +--- +Java numbers and JavaScript numbers are stored the same way, with no problems with having to cast, like with strings. This is why you can simply do something like `ModAPI.player.motionY = 99999`, without having to do any conversion. + + +--- +Booleans in Java are stored as a number, where `1` means `true` and `0` means `false`. There are no functions for converting inbetween these, because it is very easy to do so (unlike strings). To convert a javascript boolean into a java boolean simply multiply you boolean by 1. + +Eg: +```javascript +var myBool = true; +console.log(myBool * 1); +// logs '1' + +var myBool = false; +console.log(myBool * 1); +// logs '0' +``` + +Better yet, if you need to use booleans very often, just store them as numbers directly in javascript. JavaScript if statements already recognise `0` as false, so something like: +```javascript +var condition = 0; +if (condition) { + console.log("yes"); +} else { + console.log("no"); +} +// outputs 'no' +``` +will work out of the box. + +## Accessing raw data +In ModAPI's architecture, when you request an object like `ModAPI.player`, instead of giving you `ModAPI.mcinstance.$thePlayer`, it will return a `TeaVM_to_Recursive_BaseData_ProxyConf` proxy. These automatically remove the `$` prefixes, make instance methods run with the actaul object, and a variety other features. + +However, when calling methods via `ModAPI.hooks`, `ModAPI.reflect`, or even just running a method that takes in object arguments on something like `ModAPI.player`, passing in these ModAPI proxies will cause an error. + +To pass in raw java data simply call `getRef()` on the proxym which will return the raw, unmodified version of it. + +For example, take the method `setRenderViewEntity()` on `ModAPI.mcinstance`. Instead of passing an entity from `ModAPI.world.loadedEntityList.get(index)` directly, you need to use `ModAPI.world.loadedEntityList.get(index).getRef()`. Demo code: +```javascript +var entityIndex = 1; //Index of the entity to look for. 0 means first, which is usually the player, so 1 is usually a natural entity. +ModAPI.mc.setRenderViewEntity(ModAPI.world.loadedEntityList.get(entityIndex).getRef()); +``` \ No newline at end of file diff --git a/examplemods/talkback.js b/examplemods/talkback.js index d6155fb..937483b 100644 --- a/examplemods/talkback.js +++ b/examplemods/talkback.js @@ -4,9 +4,9 @@ if (event.command.toLowerCase().startsWith("/talkback")) { var message = event.command.substring("/talkback ".length); if ( - ModAPI.hooks._teavm.$rt_isInstance(event.sender, ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.entity.player.EntityPlayerMP")].class) + ModAPI.reflect.getClassById("net.minecraft.entity.player.EntityPlayerMP").instanceOf(event.sender) ) { - event.sender.addChatMessage(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.util.ChatComponentText")].constructors[0](ModAPI.util.str(message.toUpperCase()))); + event.sender.addChatMessage(ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText").constructors[0](ModAPI.util.str(message.toUpperCase()))); } event.preventDefault = true; } diff --git a/postinit.injector.js b/postinit.injector.js index 22e6450..d304685 100644 --- a/postinit.injector.js +++ b/postinit.injector.js @@ -247,7 +247,14 @@ globalThis.modapi_postinit = `(() => { } if (outputValue && typeof outputValue === "function") { return function (...args) { - return outputValue.apply(target, args); + var xOut = outputValue.apply(target, args); + if (xOut && typeof xOut === "object" && Array.isArray(xOut.data) && typeof outputValue.type === "function") { + return new Proxy(xOut.data, TeaVMArray_To_Recursive_BaseData_ProxyConf); + } + if (xOut && typeof xOut === "object" && !Array.isArray(xOut)) { + return new Proxy(xOut, TeaVM_to_Recursive_BaseData_ProxyConf); + } + return xOut; } } return outputValue; @@ -366,6 +373,9 @@ globalThis.modapi_postinit = `(() => { if (ModAPI.required.has("network") && ModAPI.javaClient && ModAPI.javaClient.$thePlayer && ModAPI.javaClient.$thePlayer.$sendQueue) { ModAPI.network = new Proxy(ModAPI.javaClient.$thePlayer.$sendQueue, TeaVM_to_Recursive_BaseData_ProxyConf); } + if (ModAPI.required.has("world") && ModAPI.javaClient && ModAPI.javaClient.$theWorld) { + ModAPI.world = new Proxy(ModAPI.javaClient.$theWorld, TeaVM_to_Recursive_BaseData_ProxyConf); + } try { ModAPI.events.callEvent("update"); } catch (error) { diff --git a/postinit.js b/postinit.js index 36da9ff..17f6161 100644 --- a/postinit.js +++ b/postinit.js @@ -247,7 +247,14 @@ } if (outputValue && typeof outputValue === "function") { return function (...args) { - return outputValue.apply(target, args); + var xOut = outputValue.apply(target, args); + if (xOut && typeof xOut === "object" && Array.isArray(xOut.data) && typeof outputValue.type === "function") { + return new Proxy(xOut.data, TeaVMArray_To_Recursive_BaseData_ProxyConf); + } + if (xOut && typeof xOut === "object" && !Array.isArray(xOut)) { + return new Proxy(xOut, TeaVM_to_Recursive_BaseData_ProxyConf); + } + return xOut; } } return outputValue; @@ -366,6 +373,9 @@ if (ModAPI.required.has("network") && ModAPI.javaClient && ModAPI.javaClient.$thePlayer && ModAPI.javaClient.$thePlayer.$sendQueue) { ModAPI.network = new Proxy(ModAPI.javaClient.$thePlayer.$sendQueue, TeaVM_to_Recursive_BaseData_ProxyConf); } + if (ModAPI.required.has("world") && ModAPI.javaClient && ModAPI.javaClient.$theWorld) { + ModAPI.world = new Proxy(ModAPI.javaClient.$theWorld, TeaVM_to_Recursive_BaseData_ProxyConf); + } try { ModAPI.events.callEvent("update"); } catch (error) {