Merge pull request #57 from eaglerforge/main

EFI v2.7
This commit is contained in:
ZXMushroom63 2025-02-02 15:22:27 +08:00 committed by GitHub
commit 2618cefdc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 599 additions and 56 deletions

View File

@ -1,5 +1,5 @@
# EaglerForgeInjector # EaglerForgeInjector
An advanced modding API injector for vanilla eaglercraft builds. An advanced modding API injector for unminified, unobfuscated, unsigned eaglercraft builds.
Current features: Current features:
- Method hooking/monkey patching - Method hooking/monkey patching
- Reflection - Reflection
@ -13,5 +13,11 @@ Go to https://eaglerforge.github.io/EaglerForgeInjector/ and upload an unminifie
#### Portable Offline #### 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. 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 ## Discord server
[https://discord.gg/rbxN7kby5W](https://discord.gg/rbxN7kby5W) [https://discord.gg/rbxN7kby5W](https://discord.gg/rbxN7kby5W)

View File

@ -1,8 +1,10 @@
## ModAPI.keygen ## 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: Methods:
- `ModAPI.keygen.item(itemId: String) : number` - `ModAPI.keygen.item(itemId: String) : number`
- Example usage is: `var id = ModAPI.keygen.item("my_example_item");` - Example usage is: `var id = ModAPI.keygen.item("my_example_item");`
- `ModAPI.keygen.block(blockId: String) : number` - `ModAPI.keygen.block(blockId: String) : number`
- Example usage is: `var id = ModAPI.keygen.block("my_example_block");` - 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");`

View File

@ -6,6 +6,7 @@ Properties:
- `classes: ReflectClass[]` - `classes: ReflectClass[]`
- `ModAPI.reflect.classes` is an array of ReflectClasses, representing (almost) every java class. - `ModAPI.reflect.classes` is an array of ReflectClasses, representing (almost) every java class.
- `classMap: Map<String, ReflectClass>` is a map of every class.
Methods: Methods:
@ -16,12 +17,17 @@ Methods:
- This method is used to find a class by its id. - 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")` - 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. - 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`. - 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` - `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. - 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) - [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 ### ReflectClass Definition
Each `ReflectClass` has the following properties: Each `ReflectClass` has the following properties:
@ -49,11 +55,8 @@ Each `ReflectClass` has the following properties:
- List of all the static variable names for the class. - List of all the static variable names for the class.
- `staticVariables: Map<String, *>` - `staticVariables: Map<String, *>`
- key-value dictionary of all the static variables in a class. - key-value dictionary of all the static variables in a class.
- `superclass: Class?` - `superclass: ReflectClass?`
- The raw teavm class of the superclass. - 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`
Each `ReflectClass` has the following methods: Each `ReflectClass` has the following methods:

View File

@ -82,3 +82,6 @@ Methods:
- Gets a block by it's ID - Gets a block by it's ID
- `ModAPI.util.getBlockFromItem(item: Item) : Block` - `ModAPI.util.getBlockFromItem(item: Item) : Block`
- Gets a block from an ItemBlock instance. - 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.

View File

@ -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", async () => { //Add an asyncronous listener to AsyncSink loading.
ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{ ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{
//when asyncsink yells at us to register the custom block, register it //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 AsyncSink.L10N.set("tile.custom_block.name", "My Custom Block"); //Set the name of the block

View File

@ -4,6 +4,7 @@ const asyncSinkIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAY
ModAPI.meta.icon(asyncSinkIcon); ModAPI.meta.icon(asyncSinkIcon);
ModAPI.meta.credits("By ZXMushroom63"); ModAPI.meta.credits("By ZXMushroom63");
(function AsyncSinkFn() { (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 //AsyncSink is a plugin to debug and override asynchronous methods in EaglercraftX
async function runtimeComponent() { async function runtimeComponent() {
const booleanResult = (b) => ModAPI.hooks.methods.nlevit_BooleanResult__new(b * 1); const booleanResult = (b) => ModAPI.hooks.methods.nlevit_BooleanResult__new(b * 1);
@ -43,6 +44,17 @@ ModAPI.meta.credits("By ZXMushroom63");
AsyncSink.L10N = new Map(); AsyncSink.L10N = new Map();
AsyncSink.FSOverride = new Set(); AsyncSink.FSOverride = new Set();
AsyncSink.MIDDLEWARE = []; 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) { AsyncSink.setFile = function setFile(path, data) {
if (typeof data === "string") { if (typeof data === "string") {
data = encoder.encode(data).buffer; data = encoder.encode(data).buffer;
@ -58,6 +70,12 @@ ModAPI.meta.credits("By ZXMushroom63");
return true; return true;
} }
AsyncSink.hideFile = function hideFile(path) {
AsyncSink.FSOverride.add(path);
AsyncSink.FS.delete(path);
return true;
}
AsyncSink.getFile = function getFile(path) { AsyncSink.getFile = function getFile(path) {
return AsyncSink.FS.get(path) || new ArrayBuffer(0); return AsyncSink.FS.get(path) || new ArrayBuffer(0);
} }
@ -160,6 +178,16 @@ ModAPI.meta.credits("By ZXMushroom63");
return originalL10NRead.apply(this, args); 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 L10NCheck = ModAPI.util.getMethodFromPackage("net.minecraft.util.StatCollector", "canTranslate");
const originalL10NCheck = ModAPI.hooks.methods[L10NCheck]; const originalL10NCheck = ModAPI.hooks.methods[L10NCheck];
ModAPI.hooks.methods[L10NCheck] = function (...args) { ModAPI.hooks.methods[L10NCheck] = function (...args) {
@ -257,6 +285,7 @@ ModAPI.meta.credits("By ZXMushroom63");
ModAPI.mc.renderItem.itemModelMesher.simpleShapesCache.clear(); ModAPI.mc.renderItem.itemModelMesher.simpleShapesCache.clear();
ModAPI.promisify(ModAPI.mc.refreshResources)().then(() => { ModAPI.promisify(ModAPI.mc.refreshResources)().then(() => {
ModAPI.events.callEvent("custom:asyncsink_reloaded", {}); ModAPI.events.callEvent("custom:asyncsink_reloaded", {});
ModAPI.events.callEvent("lib:asyncsink:registeritems", ModAPI.mc.renderItem);
}); });
} }
}); });
@ -268,4 +297,69 @@ ModAPI.meta.credits("By ZXMushroom63");
ModAPI.events.callEvent("lib:asyncsink:registeritems", ModAPI.util.wrap(args[0])); 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);
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.");
AsyncSink.Audio.Objects.forEach(pair => {
registry.put(pair[0], pair[1]);
});
}
// 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 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;
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();
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));
var soundPool = values.map(se => {
var path = ResourceLocation(ModAPI.util.str(se.path));
return SoundPoolEntry(path, se.pitch, se.volume, 1 * se.streaming);
}).map(spe => {
return makeSoundEventAccessor(spe, 1); // 1 = weight
});
var compositeSound = makeSoundEventAccessorComposite(rKey, 1, 1, category);
var compositeSoundWrapped = ModAPI.util.wrap(compositeSound);
soundPool.forEach(sound => {
compositeSoundWrapped.soundPool.add(sound);
});
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)));
}
})(); })();

View File

@ -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", () => { ModAPI.addEventListener("lib:libcustomitems:loaded", () => {
console.log("Registered my cool custom item."); console.log("Registered my cool custom item.");
LibCustomItems.registerItem({ LibCustomItems.registerItem({

209
examplemods/DuckMod.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -92,7 +92,7 @@
ModAPI.addEventListener("lib:asyncsink", async () => { //Add an asyncronous listener to AsyncSink loading. ModAPI.addEventListener("lib:asyncsink", async () => { //Add an asyncronous listener to AsyncSink loading.
ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{ 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 AsyncSink.L10N.set("tile.custom_block.name", "My Custom Block"); //Set the name of the block

28
examplemods/afkmod.js Normal file
View File

@ -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("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("Deactivated anti-afk mod.");
}
e.preventDefault = true;
}
});
})();

View File

@ -43,7 +43,7 @@ function registerSteveClientSide() {
itemClass.staticMethods.registerItemBlock0.method(block_of_steve); itemClass.staticMethods.registerItemBlock0.method(block_of_steve);
ModAPI.addEventListener("lib:asyncsink", async () => { ModAPI.addEventListener("lib:asyncsink", async () => {
ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{ 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.L10N.set("tile.steve.name", "Block Of Steve");
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/steve.json", JSON.stringify( AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/steve.json", JSON.stringify(

View File

@ -34,7 +34,7 @@ function registerSteveClientSide() {
ModAPI.addEventListener("lib:asyncsink", async () => { ModAPI.addEventListener("lib:asyncsink", async () => {
ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{ 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.L10N.set("tile.steve.name", "Block Of Steve");
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/steve.json", JSON.stringify( AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/steve.json", JSON.stringify(

135
examplemods/cubeentity.js Normal file
View File

@ -0,0 +1,135 @@
(function CubeEntity() {
ModAPI.meta.title("Cube Entity");
ModAPI.meta.version("v0");
ModAPI.meta.description("testing custom entities");
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.$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 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
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.$entityInit = function () {
console.log("Cube entity created!");
};
// 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(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;
this.$textureHeight = 64;
this.$cubeRenderer = ModelRenderer(this).$setTextureOffset(0, 0);
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, degToRad) {
this.$cubeRenderer.$render(degToRad);
}
// END CUSTOM MODEL
// START CUSTOM RENDERER
var renderClass = ModAPI.reflect.getClassById("net.minecraft.client.renderer.entity.Render");
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);
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 + 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(
ModAPI.util.asClass(nme_EntityCube),
{
$createEntity: function ($worldIn) {
return new nme_EntityCube($worldIn);
}
},
ModAPI.util.str("Cube"),
ID,
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 () => {
AsyncSink.L10N.set("entity.Cube.name", "Cube (TM)");
});
return {
EntityCube: nme_EntityCube,
ModelCube: nmcm_ModelCube,
RenderCube: nmcre_RenderCube,
cubeTexture: cubeTextures
}
}
ModAPI.dedicatedServer.appendCode(registerEntity);
var data = registerEntity();
ModAPI.addEventListener("lib:asyncsink", async () => {
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);
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);

View File

@ -5,6 +5,8 @@
ModAPI.meta.icon(itemTexture); ModAPI.meta.icon(itemTexture);
ModAPI.meta.description("Requires AsyncSink."); ModAPI.meta.description("Requires AsyncSink.");
ModAPI.require("player");
function PistolItem() { function PistolItem() {
var recoilSpeed = 0; //recoil controller var recoilSpeed = 0; //recoil controller
var DamageSourceClass = ModAPI.reflect.getClassByName("DamageSource"); var DamageSourceClass = ModAPI.reflect.getClassByName("DamageSource");

View File

@ -5,6 +5,8 @@
ModAPI.meta.icon(itemTexture); ModAPI.meta.icon(itemTexture);
ModAPI.meta.description("Requires AsyncSink."); ModAPI.meta.description("Requires AsyncSink.");
ModAPI.require("player");
function PistolItem() { function PistolItem() {
var recoilSpeed = 0; //recoil controller var recoilSpeed = 0; //recoil controller
var DamageSourceClass = ModAPI.reflect.getClassByName("DamageSource"); var DamageSourceClass = ModAPI.reflect.getClassByName("DamageSource");

View File

@ -87,7 +87,7 @@
var block_of_unluckiness = UnluckyBlocks(); var block_of_unluckiness = UnluckyBlocks();
ModAPI.addEventListener("lib:asyncsink", async () => { ModAPI.addEventListener("lib:asyncsink", async () => {
ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{ 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.L10N.set("tile.unluckiness.name", "Unlucky Block");
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/unluckiness.json", JSON.stringify( AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/unluckiness.json", JSON.stringify(

View File

@ -151,6 +151,7 @@
ModAPI.hooks ||= {}; ModAPI.hooks ||= {};
ModAPI.hooks.freezeCallstack = false; ModAPI.hooks.freezeCallstack = false;
ModAPI.hooks._rippedData ||= []; ModAPI.hooks._rippedData ||= [];
ModAPI.hooks._rippedInterfaceMap ||= {};
ModAPI.hooks._teavm ||= {}; ModAPI.hooks._teavm ||= {};
ModAPI.hooks._rippedConstructors ||= {}; ModAPI.hooks._rippedConstructors ||= {};
ModAPI.hooks._rippedInternalConstructors ||= {}; ModAPI.hooks._rippedInternalConstructors ||= {};

View File

@ -1,4 +1,4 @@
globalThis.ModAPIVersion = "v2.6"; globalThis.ModAPIVersion = "v2.7";
globalThis.doEaglerforge = true; globalThis.doEaglerforge = true;
document.querySelector("title").innerText = `EaglerForge Injector ${ModAPIVersion}`; document.querySelector("title").innerText = `EaglerForge Injector ${ModAPIVersion}`;
document.querySelector("h1").innerText = `EaglerForge Injector ${ModAPIVersion}`; document.querySelector("h1").innerText = `EaglerForge Injector ${ModAPIVersion}`;
@ -205,6 +205,7 @@ var main;(function(){`
}).filter(x => { }).filter(x => {
return (!x.includes("$_clinit_$")) && (!x.includes("$lambda$")) return (!x.includes("$_clinit_$")) && (!x.includes("$lambda$"))
}); });
//Also stores classes from $rt_classWithoutFields(0)
patchedFile = patchedFile.replaceAll( patchedFile = patchedFile.replaceAll(
/var \S+?_\S+? = \$rt_classWithoutFields\(\S*?\);/gm, /var \S+?_\S+? = \$rt_classWithoutFields\(\S*?\);/gm,
function (match) { function (match) {
@ -213,6 +214,7 @@ var main;(function(){`
"" ""
); );
var entries = []; var entries = [];
staticVariables.forEach((entry) => { staticVariables.forEach((entry) => {
if (entry.startsWith(prefix)) { if (entry.startsWith(prefix)) {
var variableName = entry var variableName = entry
@ -229,8 +231,11 @@ var main;(function(){`
}); });
var proxy = entriesToStaticVariableProxy(entries, prefix, clinitList); var proxy = entriesToStaticVariableProxy(entries, prefix, clinitList);
var shortPrefix = prefix.replace(
return match + proxy; "var ",
""
);
return match + `ModAPI.hooks._rippedInterfaceMap[\`${shortPrefix}\`]=${shortPrefix};` + proxy;
} }
); );
//Edge cases. sigh //Edge cases. sigh

View File

@ -166,6 +166,8 @@ globalThis.modapi_postinit = "(" + (() => {
return name; return name;
} }
ModAPI.util.asClass = ModAPI.hooks._teavm.$rt_cls;
ModAPI.util.wrap = function (outputValue, target, corrective, disableFunctions) { ModAPI.util.wrap = function (outputValue, target, corrective, disableFunctions) {
target ||= {}; target ||= {};
corrective ||= false; corrective ||= false;
@ -187,9 +189,9 @@ globalThis.modapi_postinit = "(" + (() => {
if (!disableFunctions && outputValue && typeof outputValue === "function" && target) { if (!disableFunctions && outputValue && typeof outputValue === "function" && target) {
return function (...args) { return function (...args) {
var xOut = outputValue.apply(target, 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) { 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); return new Proxy(xOut.data, ModAPI.util.TeaVMArray_To_Recursive_BaseData_ProxyConf);
} }
@ -267,6 +269,7 @@ globalThis.modapi_postinit = "(" + (() => {
ModAPI.hooks._rippedConstructorKeys = Object.keys(ModAPI.hooks._rippedConstructors); ModAPI.hooks._rippedConstructorKeys = Object.keys(ModAPI.hooks._rippedConstructors);
ModAPI.hooks._rippedInternalConstructorKeys = Object.keys(ModAPI.hooks._rippedInternalConstructors); ModAPI.hooks._rippedInternalConstructorKeys = Object.keys(ModAPI.hooks._rippedInternalConstructors);
ModAPI.hooks._rippedMethodKeys = Object.keys(ModAPI.hooks._rippedMethodTypeMap); ModAPI.hooks._rippedMethodKeys = Object.keys(ModAPI.hooks._rippedMethodTypeMap);
ModAPI.hooks._rippedInterfaceKeys = Object.keys(ModAPI.hooks._rippedInterfaceMap);
var compiledNames = new Set(); var compiledNames = new Set();
var metaMap = {}; var metaMap = {};
@ -300,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 //Initialise all compiled names into the class map
compiledNames.forEach(compiledName => { compiledNames.forEach(compiledName => {
var item = metaMap[compiledName]; var item = metaMap[compiledName] || ModAPI.hooks._rippedInterfaceMap[compiledName];
var classId = item?.$meta?.name || null; var classId = item?.$meta?.name || null;
if (!ModAPI.hooks._classMap[compiledName]) { if (!ModAPI.hooks._classMap[compiledName]) {
@ -319,7 +331,7 @@ globalThis.modapi_postinit = "(" + (() => {
"staticVariables": {}, "staticVariables": {},
"staticVariableNames": [], "staticVariableNames": [],
"class": item || null, "class": item || null,
"hasMeta": !!item, "hasMeta": !!(item?.$meta),
"instanceOf": function (object) { "instanceOf": function (object) {
try { try {
return ModAPI.hooks._teavm.$rt_isInstance(object, item || null); return ModAPI.hooks._teavm.$rt_isInstance(object, item || null);
@ -341,15 +353,14 @@ globalThis.modapi_postinit = "(" + (() => {
return this.constructors[i]; return this.constructors[i];
} }
} }
},
} }
} }
}
if (typeof item?.$meta?.superclass === "function" && item?.$meta?.superclass?.$meta) { if (typeof item?.$meta?.superclass === "function" && item?.$meta?.superclass?.$meta) {
ModAPI.hooks._classMap[compiledName].superclassName = item.$meta.superclass.$meta.name; ModAPI.hooks._classMap[compiledName].superclassRaw = item.$meta.superclass;
ModAPI.hooks._classMap[compiledName].superclass = item.$meta.superclass;
} else { } else {
ModAPI.hooks._classMap[compiledName].superclass = null; ModAPI.hooks._classMap[compiledName].superclassRaw = null;
ModAPI.hooks._classMap[compiledName].superclassName = null;
} }
if (item?.["$$constructor$$"]) { if (item?.["$$constructor$$"]) {
@ -385,9 +396,14 @@ globalThis.modapi_postinit = "(" + (() => {
//Prototype Injection, allows for far easier access to methods //Prototype Injection, allows for far easier access to methods
if (typeof item === "function" && ModAPI.hooks._rippedMethodTypeMap[method] === "instance") { 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]); 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;
}
} }
} }
}); });
@ -396,7 +412,19 @@ globalThis.modapi_postinit = "(" + (() => {
})); }));
ModAPI.hooks._classMap[compiledName].staticVariableNames = Object.keys(ModAPI.hooks._classMap[compiledName].staticVariables); 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.classes = Object.values(ModAPI.hooks._classMap);
ModAPI.reflect.classMap = ModAPI.hooks._classMap;
console.log("[ModAPI] Regenerated hook classmap."); console.log("[ModAPI] Regenerated hook classmap.");
} }
ModAPI.hooks.regenerateClassMap(); ModAPI.hooks.regenerateClassMap();
@ -411,8 +439,14 @@ globalThis.modapi_postinit = "(" + (() => {
//Magical function for making a subclass with a custom constructor that you can easily use super(...) on. //Magical function for making a subclass with a custom constructor that you can easily use super(...) on.
ModAPI.reflect.getSuper = function getSuper(reflectClass, filter) { ModAPI.reflect.getSuper = function getSuper(reflectClass, filter) {
filter ||= () => true; filter ||= (x) => x.length === 1;
while (!reflectClass.internalConstructors.find(filter) && reflectClass.superclass) {
reflectClass = reflectClass.superclass;
}
var initialiser = reflectClass.internalConstructors.find(filter); 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) { return function superFunction(thisArg, ...extra_args) {
reflectClass.class.call(thisArg); reflectClass.class.call(thisArg);
initialiser(thisArg, ...extra_args); initialiser(thisArg, ...extra_args);
@ -427,18 +461,27 @@ globalThis.modapi_postinit = "(" + (() => {
item: null, item: null,
supertypes: [reflectClass.class] supertypes: [reflectClass.class]
}; };
classFn.classObject = null;
}
ModAPI.reflect.implements = function impl(classFn, reflectClass) {
classFn.$meta ||= {};
classFn.$meta.supertypes ||= [];
if (reflectClass && reflectClass.class) {
classFn.$meta.supertypes.push(reflectClass.class);
}
} }
var reloadDeprecationWarnings = 0; var reloadDeprecationWarnings = 0;
const TeaVMArray_To_Recursive_BaseData_ProxyConf = { const TeaVMArray_To_Recursive_BaseData_ProxyConf = {
get(target, prop, receiver) { get(target, prop, receiver) {
var corrective = !!this._corrective;
if (prop === "getRef") { if (prop === "getRef") {
return function () { return function () {
return target; return target;
} }
} }
var outputValue = Reflect.get(target, prop, receiver); 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) { if (wrapped) {
return wrapped; return wrapped;
} }
@ -460,6 +503,7 @@ globalThis.modapi_postinit = "(" + (() => {
return ("$" + prop) in target; return ("$" + prop) in target;
}, },
get(target, prop, receiver) { get(target, prop, receiver) {
var corrective = !!this._corrective;
if (prop === "getCorrective") { if (prop === "getCorrective") {
return function () { return function () {
return new Proxy(target, patchProxyConfToCorrective(TeaVM_to_Recursive_BaseData_ProxyConf)); return new Proxy(target, patchProxyConfToCorrective(TeaVM_to_Recursive_BaseData_ProxyConf));
@ -467,7 +511,7 @@ globalThis.modapi_postinit = "(" + (() => {
} }
if (prop === "isCorrective") { if (prop === "isCorrective") {
return function () { return function () {
return !!this._corrective; return corrective;
} }
} }
if (prop === "getRef") { if (prop === "getRef") {
@ -484,22 +528,26 @@ globalThis.modapi_postinit = "(" + (() => {
} }
} }
if (prop === "isModProxy") { if (prop === "isModProxy") {
return true return true;
} }
var outProp = "$" + prop; var outProp = "$" + prop;
if (this._corrective) { if (corrective) {
outProp = ModAPI.util.getNearestProperty(target, outProp); outProp = ModAPI.util.getNearestProperty(target, outProp);
} }
var outputValue = Reflect.get(target, outProp, receiver); 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) { if (wrapped) {
return wrapped; return wrapped;
} }
return outputValue; return outputValue;
}, },
set(object, prop, value) { set(object, prop, value) {
var corrective = !!this._corrective;
var outProp = "$" + prop; var outProp = "$" + prop;
if (corrective) {
outProp = ModAPI.util.getNearestProperty(object, outProp);
}
object[outProp] = value; object[outProp] = value;
return true; return true;
}, },
@ -1019,9 +1067,9 @@ globalThis.modapi_postinit = "(" + (() => {
ModAPI.util.getBlockFromItem = easyStaticMethod("net.minecraft.block.Block", "getBlockFromItem", true); ModAPI.util.getBlockFromItem = easyStaticMethod("net.minecraft.block.Block", "getBlockFromItem", true);
ModAPI.util.getIdFromBlock = easyStaticMethod("net.minecraft.block.Block", "getIdFromBlock", true); ModAPI.util.getIdFromBlock = easyStaticMethod("net.minecraft.block.Block", "getIdFromBlock", true);
function qhash(txt, arr) { function qhash(txt, arr, interval) {
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) { if (arr.length >= interval) {
console.error("[ModAPI.keygen] Ran out of IDs while generating for " + txt); console.error("[ModAPI.keygen] Ran out of IDs while generating for " + txt);
return -1; return -1;
} }
@ -1049,10 +1097,15 @@ globalThis.modapi_postinit = "(" + (() => {
} }
ModAPI.keygen.item = function (item) { ModAPI.keygen.item = function (item) {
var values = [...ModAPI.reflect.getClassById("net.minecraft.item.Item").staticVariables.itemRegistry.$modapi_specmap.values()]; 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) { ModAPI.keygen.block = function (block) {
var values = [...ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry.$modapi_specmap.values()]; 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, 127);
} }
}).toString() + ")();"; }).toString() + ")();";