Skip to content

Commit

Permalink
Merge pull request #28 from eaglerforge/main
Browse files Browse the repository at this point in the history
Better documentation
  • Loading branch information
ZXMushroom63 authored Oct 27, 2024
2 parents 09c08bf + 512941f commit c3c1d4b
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 3 deletions.
17 changes: 17 additions & 0 deletions docs/apidoc/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ It has quite a few propeties, but most of them are just foundations for more use

### 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.
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:
- `ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("com.package.abc.MyClass", "myMethod")] = function () {}`
Expand All @@ -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
`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.
4 changes: 3 additions & 1 deletion docs/apidoc/reflect.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,6 @@ Each `ReflectClass` has the following methods:
- `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 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 changes: 143 additions & 0 deletions examplemods/dupe_hunting.js
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions examplemods/f11fix.js
Original file line number Diff line number Diff line change
@@ -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();
}
}
})();
11 changes: 11 additions & 0 deletions examplemods/lib.customitems.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
globalThis.LCI_RMBEVENTS ||= {};
globalThis.LCI_LMBEVENTS ||= {};
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 oldUse = ModAPI.hooks.methods[useName];
ModAPI.hooks.methods[useName] = function ($this, packet) {
Expand Down Expand Up @@ -94,6 +100,7 @@
globalThis.LCI_RMBEVENTS ||= {};
globalThis.LCI_LMBEVENTS ||= {};
globalThis.LCI_RECIPEEVENTS ||= {};
globalThis.LCI_ITEMDB ||= {};
globalThis.LCI_REGISTRY.push(data.tag);
if (data.onRightClickGround) {
globalThis.LCI_RMBEVENTS[data.tag] = new Function("user", "world", "itemstack", "blockpos", data.onRightClickGround);
Expand Down Expand Up @@ -141,6 +148,7 @@
if (globalThis.LCI_RECIPEEVENTS[data.tag]) {
globalThis.LCI_RECIPEEVENTS[data.tag](new Proxy(testItem, ModAPI.util.TeaVM_to_Recursive_BaseData_ProxyConf));
}
globalThis.LCI_ITEMBD[data.tag] = new Proxy(testItem, ModAPI.util.TeaVM_to_Recursive_BaseData_ProxyConf);

var craftingManager = ModAPI.reflect.getClassById("net.minecraft.item.crafting.CraftingManager").staticMethods.getInstance.method();
if((data.useRecipe !== false) || (data.useRecipe !== "false")) {
Expand All @@ -153,5 +161,8 @@
LibCustomItems.registerItem = function register(data) {
LCI_registerItem(data);
}
LibCustomItems.makeItemStack = function makeItemStack(tag) {
return globalThis.LCI_ITEMBD[tag] || null;
}
ModAPI.events.callEvent("lib:libcustomitems:loaded", {});
})();
3 changes: 1 addition & 2 deletions examplemods/slippery.js

Large diffs are not rendered by default.

0 comments on commit c3c1d4b

Please sign in to comment.