Merge pull request #28 from eaglerforge/main

Better documentation
This commit is contained in:
ZXMushroom63 2024-10-27 16:39:42 +08:00 committed by GitHub
commit c3c1d4b4c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 187 additions and 3 deletions

View File

@ -4,6 +4,21 @@ It has quite a few propeties, but most of them are just foundations for more use
### Property: ModAPI.hooks.methods ### 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. `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.
It's properties are defined by the package of the java class, as well as that methods name. For example, `net.minecraft.world.World`'s `spawnEntityInWorld` method will be named `ModAPI.hooks.methods.nmw_World_spawnEntityInWorld`, where `nmw` is `net.minecraft.world`. You can automate finding this by using `ModAPI.util.getMethodFromPackage("com.package.abc.MyClass", "myMethod")`, which is an alorithm that automatically finds the key in `ModAPI.hooks.methods`.
Due to the injector's patches, all game code runs by calling these ModAPI.hooks.methods functions, which allows you to call, patch, replace and modify the game's source code in realtime.
To call a method, use `ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("com.package.abc.MyClass", "myMethod")](argument1, argument2, etc);`.
Complexities with calling methods:
Methods that are described in EaglercraftX's source code as `static` can be called by replacing boolean values `true`/`false` with `1` and `0`, respectively. Strings have to be converted by using `ModAPI.util.str("string")` or one of it's aliases. To pass data from a ModAPI proxy you have to use .getRef(), eg: `ModAPI.player.getRef(); //raw java player object`.
Non static methods must be called with the object they are running on (the value of the `this` keyword in the java source). Do this by using the object as the first argument. For example, look at this code:
```javascript
ModAPI.require("world");
ModAPI.hooks.methods.nmw_World_spawnEntityInWorld(ModAPI.world.getRef(), *an entity object*);
```
- To replace a method with another, you can use: - To replace a method with another, you can use:
- `ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("com.package.abc.MyClass", "myMethod")] = function () {}` - `ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("com.package.abc.MyClass", "myMethod")] = function () {}`
@ -19,5 +34,7 @@ It has quite a few propeties, but most of them are just foundations for more use
} }
``` ```
Please note that if you are just calling methods, I recommend attempting to call the method directly on the object. `Eg: ModAPI.world.spawnEntityInWorld(*an entity object*)` or using `ModAPI.reflect.getClassByName("MyClass").methods.methodName.method()`;
### Property: ModAPI.hooks._teavm ### 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. `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.

View File

@ -66,4 +66,6 @@ Each `ReflectClass` has the following methods:
- `method(...)` - `method(...)`
- This is the java method. - This is the java method.
- If it is an instance method (accessed from a ReflectClasses' `methods` property), the first argument should be an instance of the class. Eg: `ModAPI.reflect.getClassByName("EntityPlayerSP").methods.closeScreen.method(ModAPI.player.getRef())` - If it is an instance method (accessed from a ReflectClasses' `methods` property), the first argument should be an instance of the class. Eg: `ModAPI.reflect.getClassByName("EntityPlayerSP").methods.closeScreen.method(ModAPI.player.getRef())`
- If it is a static method (accessed from a ReflectClasses' `staticMethods` property), call the method as usual. Eg: `ModAPI.reflect.getClassById("net.minecraft.init.Items").staticMethods.getRegisteredItem.method(ModAPI.util.str("apple"))` - If it is a static method (accessed from a ReflectClasses' `staticMethods` property), call the method as usual. Eg: `ModAPI.reflect.getClassById("net.minecraft.init.Items").staticMethods.getRegisteredItem.method(ModAPI.util.str("apple"))`
Keep in mind that you need to wrap strings using `ModAPI.util.str("MyString")`, convert booleans into respective numbers (`true`->`1`, `false`->`0`), and get references of ModAPI proxies (`ModAPI.player`->`ModAPI.player.getRef()`);

143
examplemods/dupe_hunting.js Normal file
View File

@ -0,0 +1,143 @@
//utility function to return an array of GuiButtons from an array of JS objects, while automatically binding them
function button_utility_script(inputArr, bindingClass, actionBindMode) {
// By ZXMushroom63
// action bind mode:
// 0 - bind to the same as the binding class
// 1 - do not bind
// 2 - bind to GuiScreen
actionBindMode ||= 0;
var button = ModAPI.reflect.getClassById("net.minecraft.client.gui.GuiButton").constructors.find(x => x.length === 6);
var originalActionPerformed = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage(actionBindMode === 2 ? "net.minecraft.client.gui.GuiScreen" : bindingClass, "actionPerformed")];
var originalInit = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage(bindingClass, "initGui")];
var out = inputArr.flatMap(x => {
var btn = button(x.uid, x.x, x.y, x.w, x.h, ModAPI.util.str(x.text));
return btn;
});
if (actionBindMode !== 1) {
ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage(actionBindMode === 2 ? "net.minecraft.client.gui.GuiScreen" : bindingClass, "actionPerformed")] = function (...args) {
var id = ModAPI.util.wrap(args[1]).getCorrective().id;
var jsAction = inputArr.find(x => x.uid === id);
if (jsAction) {
jsAction.click(ModAPI.util.wrap(args[0]));
}
return originalActionPerformed.apply(this, args);
}
}
ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage(bindingClass, "initGui")] = function (...args) {
originalInit.apply(this, args);
var gui = ModAPI.util.wrap(args[0]).getCorrective();
out.forEach(guiButton => {
gui.buttonList.add(guiButton);
});
}
}
(() => {
ModAPI.require("player");
var backlog = [];
var delayState = false;
const originalSend = WebSocket.prototype.send;
//Override WebSocket.send, so when eagler tries to send messages, it runs our code instead
Object.defineProperty(WebSocket.prototype, 'send', {
configurable: true,
enumerable: false,
writable: false,
value: function (data) {
//If blinking, push data to backlog along with it's websocket instance.
if (delayState) {
backlog.push({ data: data, thisArg: this });
} else { //Else send the data as normal
originalSend.call(this, data);
}
}
});
ModAPI.meta.title("Dupe Hunting");
ModAPI.meta.description("⚠Only works over WS, not local. May induce bans.⚠️");
ModAPI.meta.credits("by ZXMushroom63");
var dupeHuntButtons = [{
text: "Silently Close",
click: () => {
ModAPI.minecraft.displayGuiScreen(null);
},
x: 0,
y: 0,
w: 100,
h: 20,
uid: 142715254
},
{
text: "Toggle Delay",
click: () => {
delayState = !delayState;
alert(delayState ? "Delay On" : "Sending Packets...");
if (delayState === false) {
for (let i = 0; i < backlog.length; i++) {
const backlogItem = backlog[i];
originalSend.call(backlogItem.thisArg, backlogItem.data);
}
backlog = [];
}
},
x: 0,
y: 20,
w: 100,
h: 20,
uid: 142715253
},
{
text: "Server Close",
click: () => {
var CloseWindow = ModAPI.reflect.getClassByName("C0DPacketCloseWindow").constructors.find(x => x.length === 1);
ModAPI.player.sendQueue.addToSendQueue(CloseWindow(ModAPI.player.openContainer.getCorrective().windowId));
},
x: 0,
y: 40,
w: 100,
h: 20,
uid: 142715252
},
{
text: "Send & Disconnect",
click: () => {
delayState = false;
for (let i = 0; i < backlog.length; i++) {
const backlogItem = backlog[i];
originalSend.call(backlogItem.thisArg, backlogItem.data);
}
backlog = [];
ModAPI.mc.getNetHandler().getNetworkManager().doClientDisconnect(ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.util.ChatComponentText")].constructors[0](ModAPI.util.str("Dupe Hunting Utils Disconnect")));
},
x: 0,
y: 60,
w: 100,
h: 20,
uid: 142715251
},
{
text: "Use Chat",
click: () => {
var p = window.prompt("Input chat/command:", "Hello World");
if (p) {
ModAPI.player.sendChatMessage(ModAPI.util.str(p));
}
},
x: 0,
y: 80,
w: 100,
h: 20,
uid: 142715250
}];
[
"net.minecraft.client.gui.inventory.GuiInventory",
"net.minecraft.client.gui.inventory.GuiContainerCreative",
"net.minecraft.client.gui.inventory.GuiBeacon"
].forEach(ui => {
button_utility_script(dupeHuntButtons, ui, 0);
});
})();

12
examplemods/f11fix.js Normal file
View File

@ -0,0 +1,12 @@
(() => {
ModAPI.meta.title("Fullscreen Fixer");
ModAPI.meta.credits("By ZXMushroom63");
var oldF11 = HTMLElement.prototype.requestFullscreen;
HTMLElement.prototype.requestFullscreen = function () {
if (this instanceof HTMLBodyElement) {
oldF11.apply(this, []);
} else {
document.body.requestFullscreen();
}
}
})();

View File

@ -12,6 +12,12 @@
globalThis.LCI_RMBEVENTS ||= {}; globalThis.LCI_RMBEVENTS ||= {};
globalThis.LCI_LMBEVENTS ||= {}; globalThis.LCI_LMBEVENTS ||= {};
globalThis.LCI_RECIPEEVENTS ||= {}; globalThis.LCI_RECIPEEVENTS ||= {};
globalThis.LCI_ITEMDB ||= {};
globalThis.LibCustomItems = {
makeItemStack: function makeItemStack(tag) {
return globalThis.LCI_ITEMBD[tag] || null;
}
};
var useName = ModAPI.util.getMethodFromPackage("net.minecraft.network.NetHandlerPlayServer", "processPlayerBlockPlacement"); var useName = ModAPI.util.getMethodFromPackage("net.minecraft.network.NetHandlerPlayServer", "processPlayerBlockPlacement");
var oldUse = ModAPI.hooks.methods[useName]; var oldUse = ModAPI.hooks.methods[useName];
ModAPI.hooks.methods[useName] = function ($this, packet) { ModAPI.hooks.methods[useName] = function ($this, packet) {
@ -94,6 +100,7 @@
globalThis.LCI_RMBEVENTS ||= {}; globalThis.LCI_RMBEVENTS ||= {};
globalThis.LCI_LMBEVENTS ||= {}; globalThis.LCI_LMBEVENTS ||= {};
globalThis.LCI_RECIPEEVENTS ||= {}; globalThis.LCI_RECIPEEVENTS ||= {};
globalThis.LCI_ITEMDB ||= {};
globalThis.LCI_REGISTRY.push(data.tag); globalThis.LCI_REGISTRY.push(data.tag);
if (data.onRightClickGround) { if (data.onRightClickGround) {
globalThis.LCI_RMBEVENTS[data.tag] = new Function("user", "world", "itemstack", "blockpos", data.onRightClickGround); globalThis.LCI_RMBEVENTS[data.tag] = new Function("user", "world", "itemstack", "blockpos", data.onRightClickGround);
@ -141,6 +148,7 @@
if (globalThis.LCI_RECIPEEVENTS[data.tag]) { if (globalThis.LCI_RECIPEEVENTS[data.tag]) {
globalThis.LCI_RECIPEEVENTS[data.tag](new Proxy(testItem, ModAPI.util.TeaVM_to_Recursive_BaseData_ProxyConf)); globalThis.LCI_RECIPEEVENTS[data.tag](new Proxy(testItem, ModAPI.util.TeaVM_to_Recursive_BaseData_ProxyConf));
} }
globalThis.LCI_ITEMBD[data.tag] = new Proxy(testItem, ModAPI.util.TeaVM_to_Recursive_BaseData_ProxyConf);
var craftingManager = ModAPI.reflect.getClassById("net.minecraft.item.crafting.CraftingManager").staticMethods.getInstance.method(); var craftingManager = ModAPI.reflect.getClassById("net.minecraft.item.crafting.CraftingManager").staticMethods.getInstance.method();
if((data.useRecipe !== false) || (data.useRecipe !== "false")) { if((data.useRecipe !== false) || (data.useRecipe !== "false")) {
@ -153,5 +161,8 @@
LibCustomItems.registerItem = function register(data) { LibCustomItems.registerItem = function register(data) {
LCI_registerItem(data); LCI_registerItem(data);
} }
LibCustomItems.makeItemStack = function makeItemStack(tag) {
return globalThis.LCI_ITEMBD[tag] || null;
}
ModAPI.events.callEvent("lib:libcustomitems:loaded", {}); ModAPI.events.callEvent("lib:libcustomitems:loaded", {});
})(); })();

File diff suppressed because one or more lines are too long