mirror of
https://github.com/eaglerforge/EaglerForgeInjector
synced 2025-07-26 07:19:26 -09:00
commit
bc0bba9bde
@ -1,5 +1,10 @@
|
|||||||
# EaglerForgeInjector
|
# EaglerForgeInjector
|
||||||
A modding API injector for vanilla eaglercraft builds.
|
An advanced modding API injector for vanilla eaglercraft builds.
|
||||||
|
Current features:
|
||||||
|
- Method hooking/monkey patching
|
||||||
|
- Reflection
|
||||||
|
- Custom classes
|
||||||
|
- Modify the dedicated server
|
||||||
|
|
||||||
### How to use:
|
### How to use:
|
||||||
#### Online
|
#### Online
|
||||||
|
231
docs/tutorials/custom_block.md
Normal file
231
docs/tutorials/custom_block.md
Normal file
File diff suppressed because one or more lines are too long
@ -23,7 +23,7 @@ Tutorials:
|
|||||||
- [Disable All Particles](disable_all_particles.md)
|
- [Disable All Particles](disable_all_particles.md)
|
||||||
- [Slippery Mod](slippery.md)
|
- [Slippery Mod](slippery.md)
|
||||||
- [/hat mod](hat.md)
|
- [/hat mod](hat.md)
|
||||||
- [/spawnxp command](comingsoon)
|
- [/spawnxp command](spawnxp.md)
|
||||||
|
|
||||||
### Advanced
|
### Advanced
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
@ -33,6 +33,6 @@ Prerequisites:
|
|||||||
- Your EaglerForgeInjector processed.html opened in an editor (optional)
|
- Your EaglerForgeInjector processed.html opened in an editor (optional)
|
||||||
|
|
||||||
Tutorials:
|
Tutorials:
|
||||||
- [Custom Blocks](comingsoon)
|
- [Custom Blocks](custom_block.md)
|
||||||
- [Custom Items](comingsoon)
|
- [Custom Items](comingsoon)
|
||||||
- [Timescale Command](comingsoon)
|
- [Timescale Command](comingsoon)
|
67
docs/tutorials/spawnxp.md
Normal file
67
docs/tutorials/spawnxp.md
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
## /spawnxp command
|
||||||
|
This tutorial will cover spawning in entities using the [`ModAPI.promisify()`](../apidoc/promisify.md) API.
|
||||||
|
|
||||||
|
As usual, we'll start with the boilerplate:
|
||||||
|
```javascript
|
||||||
|
(function SpawnXPMod() {
|
||||||
|
ModAPI.meta.title("Spawn XP");
|
||||||
|
ModAPI.meta.description("Adds a /spawnxp command.");
|
||||||
|
ModAPI.meta.credits("By <author_name>");
|
||||||
|
|
||||||
|
//future code
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
We'll move context to the server, and add a command listener, and ensure the sender is a player:
|
||||||
|
```javascript
|
||||||
|
PluginAPI.dedicatedServer.appendCode(function () { //Run this code on the server
|
||||||
|
PluginAPI.addEventListener("processcommand", (event) => { //Command processing event
|
||||||
|
if (
|
||||||
|
!ModAPI.reflect.getClassById("net.minecraft.entity.player.EntityPlayerMP")
|
||||||
|
.instanceOf(event.sender.getRef())
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, we'll check that the command is /spawnxp. If it is, we'll get the current dimension of the player, and spawn an XP orb at the sender's position. To construct the XP orb, we'll get the `EntityXPOrb` class and get the [first constructor](https://nurmarvin.github.io/Minecraft-1.8-JavaDocs/net/minecraft/entity/EntityXPOrb.html)
|
||||||
|
```javascript
|
||||||
|
PluginAPI.dedicatedServer.appendCode(function () { //Run this code on the server
|
||||||
|
PluginAPI.addEventListener("processcommand", (event) => { //Command processing event
|
||||||
|
if (
|
||||||
|
!ModAPI.reflect.getClassById("net.minecraft.entity.player.EntityPlayerMP") //If it isn't a player, exit the method
|
||||||
|
.instanceOf(event.sender.getRef())
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.command.toLowerCase().startWith("/spawnxp")) {
|
||||||
|
const world = event.sender.getServerForPlayer(); //get the current dimension
|
||||||
|
const senderPos = event.sender.getPosition(); //get the player's current position
|
||||||
|
|
||||||
|
const EntityXP = ModAPI.reflect.getClassByName("EntityXPOrb"); // Get the entity xp orb class
|
||||||
|
|
||||||
|
const xporb = EntityXP.constructors[0](world.getRef(), senderPos.getX(), senderPos.getY(), senderPos.getZ(), 10); //Construct the first constructor, with arguments, World, x, y, z, xp value
|
||||||
|
|
||||||
|
//Get the spawn entity in world method
|
||||||
|
var spawnEntityInWorldMethod = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.world.World", "spawnEntityInWorld")];
|
||||||
|
//Because this method reads and writes data to chunks, it tries to save and load the eaglercraft state, which won't work if eaglercraft is also running its own code.
|
||||||
|
//To work around this issue, use ModAPI.promisify() to convert the method into one that returns a promise
|
||||||
|
spawnEntityInWorldMethod = ModAPI.promisify(spawnEntityInWorldMethod);
|
||||||
|
|
||||||
|
//we can now use the spawnEntityInWorld method
|
||||||
|
//wherever you see getRef(), it means to retrieve the raw, untouched data that TeaVM can use.
|
||||||
|
spawnEntityInWorldMethod(world.getRef(), xporb).then(result => {
|
||||||
|
// Get the chat component class
|
||||||
|
const ChatComponentTextClass = ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText");
|
||||||
|
|
||||||
|
// Construct the chat component and send it to the client.
|
||||||
|
event.sender.addChatMessage(ChatComponentTextClass.constructors[0](ModAPI.util.str("An xp orb has been spawned!")));
|
||||||
|
});
|
||||||
|
|
||||||
|
//Stop the unknown command error
|
||||||
|
event.preventDefault = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
135
examplemods/Tutorial_Custom_Block.js
Normal file
135
examplemods/Tutorial_Custom_Block.js
Normal file
File diff suppressed because one or more lines are too long
@ -19,22 +19,14 @@ function fixupBlockIds() {
|
|||||||
}
|
}
|
||||||
function makeSteveBlock() {
|
function makeSteveBlock() {
|
||||||
var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block");
|
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);
|
||||||
var creativeBlockTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabBlock;
|
var creativeBlockTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabBlock;
|
||||||
var nmb_BlockSteve = function nmb_BlockSteve() {
|
function nmb_BlockSteve() {
|
||||||
blockSuper(this, ModAPI.materials.rock.getRef());
|
blockSuper(this, ModAPI.materials.rock.getRef());
|
||||||
this.$defaultBlockState = this.$blockState.$getBaseState();
|
this.$defaultBlockState = this.$blockState.$getBaseState();
|
||||||
this.$setCreativeTab(creativeBlockTab);
|
this.$setCreativeTab(creativeBlockTab);
|
||||||
}
|
}
|
||||||
ModAPI.reflect.prototypeStack(blockClass, nmb_BlockSteve);
|
ModAPI.reflect.prototypeStack(blockClass, nmb_BlockSteve);
|
||||||
nmb_BlockSteve.prototype.$isOpaqueCube = function () {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
nmb_BlockSteve.prototype.$createBlockState = function (t) {
|
|
||||||
return makeBlockState(this, ModAPI.array.object(iproperty, 0));
|
|
||||||
}
|
|
||||||
globalThis.nmb_BlockSteve = nmb_BlockSteve;
|
globalThis.nmb_BlockSteve = nmb_BlockSteve;
|
||||||
}
|
}
|
||||||
function registerSteveClientSide() {
|
function registerSteveClientSide() {
|
||||||
|
27
examplemods/entity_trace_test.js
Normal file
27
examplemods/entity_trace_test.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
(() => {
|
||||||
|
ModAPI.meta.title("/ray_trace_test");
|
||||||
|
ModAPI.meta.description("Mod to test server-side raycasting.");
|
||||||
|
ModAPI.meta.credits("By ZXMushroom63");
|
||||||
|
|
||||||
|
PluginAPI.dedicatedServer.appendCode(function () {
|
||||||
|
PluginAPI.addEventListener("processcommand", (event) => {
|
||||||
|
if (event.command.toLowerCase().startsWith("/ray_trace_test")) {
|
||||||
|
if (
|
||||||
|
ModAPI.reflect.getClassById("net.minecraft.entity.player.EntityPlayerMP").instanceOf(event.sender.getRef())
|
||||||
|
) {
|
||||||
|
//raytrace distance = 6
|
||||||
|
//the 0 on the end is for client side view bobbing (frame based). we are on the server so using 0 as default.
|
||||||
|
var movingObjectPosition = event.sender.rayTrace(6, 0).getCorrective();
|
||||||
|
console.log(movingObjectPosition);
|
||||||
|
|
||||||
|
var hitVec = movingObjectPosition.hitVec;
|
||||||
|
|
||||||
|
event.sender.addChatMessage(ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText").constructors[0](
|
||||||
|
movingObjectPosition.toString() //This is a java string, but that's ok since it goes into a java method.
|
||||||
|
));
|
||||||
|
}
|
||||||
|
event.preventDefault = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
121
examplemods/guns.js
Normal file
121
examplemods/guns.js
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
(()=>{
|
||||||
|
const itemTexture = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADySURBVFhH7ZQxDoMwDEVNr4G6VmJC6mk4Itdh7VpxjsBPDaKVCLZxypInIccg5zs/CVQoKAj8uHLjeETouo6HvlQcU3yJ932PIKkTceRAFH8NA6dE3IzbVogdQBOPtuVXfk5IJ8jWhKY4SxPaQvcmLEWuTUj/A1sqiEEcTQBENDWjvh3mvZtxccLiwMqvE0DrhNUBCCy1KSdAUsPiwFYcpM5ENrYWh3tdI4cT4dk0MSKPXzMSRSC+PMi14mcO4e7esv2iJqyHEGDVPCR6jyMC5luERXOfuoY7QFi8MKsDe6tXk8OBv7Ge/E96DZeKFwoOEE1wUX7TFh5zsgAAAABJRU5ErkJggg==";
|
||||||
|
ModAPI.meta.title("guns");
|
||||||
|
ModAPI.meta.version("v1.0");
|
||||||
|
ModAPI.meta.icon(itemTexture);
|
||||||
|
ModAPI.meta.description("Requires AsyncSink.");
|
||||||
|
|
||||||
|
function PistolItem() {
|
||||||
|
var DamageSourceClass = ModAPI.reflect.getClassByName("DamageSource");
|
||||||
|
var creativeMiscTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabMisc;
|
||||||
|
var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item");
|
||||||
|
var itemSuper = ModAPI.reflect.getSuper(itemClass, (x) => x.length === 1);
|
||||||
|
var nmi_ItemPistol = function nmi_ItemPistol() {
|
||||||
|
itemSuper(this); //Use super function to get block properties on this class.
|
||||||
|
this.$setCreativeTab(creativeMiscTab);
|
||||||
|
}
|
||||||
|
function entityRayCast(player, world, range){
|
||||||
|
var eyePosition = player.getPositionEyes(0.0);
|
||||||
|
var lookVector = player.getLook(0.0);
|
||||||
|
var targetPosition = eyePosition.addVector(lookVector.xCoord * range, lookVector.yCoord * range, lookVector.zCoord * range);
|
||||||
|
var entities = world.getEntitiesWithinAABBExcludingEntity(
|
||||||
|
player.getRef(),
|
||||||
|
player.getEntityBoundingBox().expand(range, range, range).getRef()
|
||||||
|
).getCorrective().array;
|
||||||
|
var closestEntity = null;
|
||||||
|
var closestDistance = range;
|
||||||
|
|
||||||
|
// Iterate through all entities to find the one the player is looking at
|
||||||
|
for (var i = 0; i < entities.length; i++) {
|
||||||
|
if (!entities[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var entity = entities[i];
|
||||||
|
|
||||||
|
// Check if the entity's bounding box intersects with the player's ray
|
||||||
|
var entityBB = entity.getEntityBoundingBox().expand(0.3, 0.3, 0.3);
|
||||||
|
var intercept = entityBB.calculateIntercept(eyePosition.getRef(), targetPosition.getRef());
|
||||||
|
|
||||||
|
if (intercept != null) {
|
||||||
|
var distance = eyePosition.distanceTo(intercept.hitVec.getRef());
|
||||||
|
if (distance < closestDistance) {
|
||||||
|
closestDistance = distance;
|
||||||
|
closestEntity = entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var rayTraceResult = closestEntity;
|
||||||
|
if (rayTraceResult != null){
|
||||||
|
return rayTraceResult;
|
||||||
|
} else{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ModAPI.reflect.prototypeStack(itemClass, nmi_ItemPistol);
|
||||||
|
nmi_ItemPistol.prototype.$onItemRightClick = function ($itemstack, $world, $player) {
|
||||||
|
DamageSourceClass.staticMethods.$callClinit.method();
|
||||||
|
//Noticed that the gun only worked after an entity in the world takes damage XD
|
||||||
|
//TeaVM is very optimised. Using $callClinit tells it to hurry up pretty much lol
|
||||||
|
|
||||||
|
var cactus = DamageSourceClassstaticVariables.cactus;
|
||||||
|
var world = ModAPI.util.wrap($world);
|
||||||
|
var entityplayer = ModAPI.util.wrap($player);
|
||||||
|
var shotentity = entityRayCast(entityplayer, world, 12.0)
|
||||||
|
if (shotentity != null){
|
||||||
|
shotentity.attackEntityFrom(cactus, 10);
|
||||||
|
world.playSoundAtEntity(entityplayer.getRef(), ModAPI.util.str("tile.piston.out"), 1.0, 1.8);
|
||||||
|
}
|
||||||
|
return $itemstack;
|
||||||
|
}
|
||||||
|
|
||||||
|
function internal_reg() {
|
||||||
|
var pistol_item = (new nmi_ItemPistol()).$setUnlocalizedName(
|
||||||
|
ModAPI.util.str("pistol")
|
||||||
|
);
|
||||||
|
itemClass.staticMethods.registerItem.method(ModAPI.keygen.item("pistol"), ModAPI.util.str("pistol"), pistol_item);
|
||||||
|
ModAPI.items["pistol"] = pistol_item;
|
||||||
|
|
||||||
|
return pistol_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ModAPI.items) {
|
||||||
|
return internal_reg();
|
||||||
|
} else {
|
||||||
|
ModAPI.addEventListener("bootstrap", internal_reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModAPI.dedicatedServer.appendCode(PistolItem);
|
||||||
|
var pistol_item = PistolItem();
|
||||||
|
|
||||||
|
ModAPI.addEventListener("lib:asyncsink", async () => {
|
||||||
|
ModAPI.addEventListener("custom:asyncsink_reloaded", ()=>{
|
||||||
|
ModAPI.mc.renderItem.registerItem(pistol_item, ModAPI.util.str("pistol"));
|
||||||
|
});
|
||||||
|
AsyncSink.L10N.set("item.pistol.name", "Pistol");
|
||||||
|
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/item/pistol.json", JSON.stringify(
|
||||||
|
{
|
||||||
|
"parent": "builtin/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "items/pistol"
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"thirdperson": {
|
||||||
|
"rotation": [ 5, 80, -45 ],
|
||||||
|
"translation": [ 0, 1, -3 ],
|
||||||
|
"scale": [ 1.0, 1.0, 1.0 ]
|
||||||
|
},
|
||||||
|
"firstperson": {
|
||||||
|
"rotation": [ 0, -135, 25 ],
|
||||||
|
"translation": [ 0, 4, 2 ],
|
||||||
|
"scale": [ 1.8, 1.8, 1.8 ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
));
|
||||||
|
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/items/pistol.png", await (await fetch(
|
||||||
|
itemTexture
|
||||||
|
)).arrayBuffer());
|
||||||
|
});
|
||||||
|
})();
|
@ -24,29 +24,21 @@
|
|||||||
}
|
}
|
||||||
var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item");
|
var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item");
|
||||||
var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block");
|
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); //Get super function from the block class with a target length of two. ($this (mandatory), material (optional))
|
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 creativeBlockTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabBlock;
|
||||||
var breakBlockMethod = blockClass.methods.breakBlock.method;
|
var breakBlockMethod = blockClass.methods.breakBlock.method;
|
||||||
var nmb_BlockUnlucky = function nmb_BlockUnlucky() {
|
function nmb_BlockUnlucky() {
|
||||||
blockSuper(this, ModAPI.materials.rock.getRef()); //Use super function to get block properties on this class.
|
blockSuper(this, ModAPI.materials.rock.getRef()); //Use super function to get block properties on this class.
|
||||||
this.$defaultBlockState = this.$blockState.$getBaseState();
|
this.$defaultBlockState = this.$blockState.$getBaseState();
|
||||||
this.$setCreativeTab(creativeBlockTab);
|
this.$setCreativeTab(creativeBlockTab);
|
||||||
}
|
}
|
||||||
ModAPI.reflect.prototypeStack(blockClass, nmb_BlockUnlucky);
|
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) {
|
nmb_BlockUnlucky.prototype.$breakBlock = function ($world, $blockpos, $blockstate) {
|
||||||
var world = ModAPI.util.wrap($world);
|
var world = ModAPI.util.wrap($world);
|
||||||
var blockpos = ModAPI.util.wrap($blockpos);
|
var blockpos = ModAPI.util.wrap($blockpos);
|
||||||
if (Math.random() < 1) { //was gonna add random events but couldn't be bothered. Enjoy exploding!
|
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,
|
world.newExplosion(null, blockpos.getX() + 0.5, blockpos.getY() + 0.5,
|
||||||
blockpos.getZ() + 0.5, 9, 1, 1);
|
blockpos.getZ() + 0.5, 9, 1, 0);
|
||||||
}
|
}
|
||||||
return breakBlockMethod(this, $world, $blockpos, $blockstate);
|
return breakBlockMethod(this, $world, $blockpos, $blockstate);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
var example_item = (new nmi_ItemExample()).$setUnlocalizedName(
|
var example_item = (new nmi_ItemExample()).$setUnlocalizedName(
|
||||||
ModAPI.util.str("exampleitem")
|
ModAPI.util.str("exampleitem")
|
||||||
);
|
);
|
||||||
itemClass.staticMethods.registerItem0.method(ModAPI.keygen.item("exampleitem"), ModAPI.util.str("exampleitem"), example_item);
|
itemClass.staticMethods.registerItem.method(ModAPI.keygen.item("exampleitem"), ModAPI.util.str("exampleitem"), example_item);
|
||||||
ModAPI.items["exampleitem"] = example_item;
|
ModAPI.items["exampleitem"] = example_item;
|
||||||
|
|
||||||
return example_item;
|
return example_item;
|
||||||
|
@ -117,7 +117,7 @@
|
|||||||
<summary>
|
<summary>
|
||||||
How do I compile my own unobfuscated unsigned Eaglercraft build?
|
How do I compile my own unobfuscated unsigned Eaglercraft build?
|
||||||
</summary>
|
</summary>
|
||||||
<a href="docs/compiling_client.md">tutorial here</a>
|
<a href="docs/compiling_client">tutorial here</a>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
<summary>How does this tool work?</summary>
|
<summary>How does this tool work?</summary>
|
||||||
@ -169,7 +169,7 @@
|
|||||||
|
|
||||||
<!-- Code assets -->
|
<!-- Code assets -->
|
||||||
<script src="postinit.js"></script>
|
<script src="postinit.js"></script>
|
||||||
<script src="modloader.injector.js"></script>
|
<script src="modloader.js"></script>
|
||||||
<script src="modgui.js"></script>
|
<script src="modgui.js"></script>
|
||||||
<script src="efserver.js"></script>
|
<script src="efserver.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
28
injector.js
28
injector.js
@ -18,24 +18,17 @@ function entriesToStaticVariableProxy(entries, prefix) {
|
|||||||
var getComponents = "";
|
var getComponents = "";
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
getComponents += `
|
getComponents += `
|
||||||
case \`${entry.name}\`:
|
case \`${entry.name}\`: return ${entry.variable};`;
|
||||||
return ${entry.variable};
|
|
||||||
break;`;
|
|
||||||
});
|
});
|
||||||
getComponents += `
|
getComponents += `
|
||||||
default:
|
default: return Reflect.get(a,b,c);`
|
||||||
return Reflect.get(a,b,c);`
|
|
||||||
|
|
||||||
var setComponents = "";
|
var setComponents = "";
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
setComponents += `
|
setComponents += `
|
||||||
case \`${entry.name}\`:
|
case \`${entry.name}\`: ${entry.variable} = c; break;`;
|
||||||
${entry.variable} = c;
|
|
||||||
break;`;
|
|
||||||
});
|
});
|
||||||
setComponents += `
|
setComponents += ` default: a[b]=c;`
|
||||||
default:
|
|
||||||
a[b]=c;`
|
|
||||||
/*/
|
/*/
|
||||||
|
|
||||||
ModAPI.hooks._rippedStaticIndexer[\`${prefix.replace(
|
ModAPI.hooks._rippedStaticIndexer[\`${prefix.replace(
|
||||||
@ -196,8 +189,7 @@ var main;(function(){`
|
|||||||
(match) => {
|
(match) => {
|
||||||
if (
|
if (
|
||||||
match.includes("__init_") ||
|
match.includes("__init_") ||
|
||||||
match.includes("__clinit_") ||
|
match.includes("__clinit_")
|
||||||
match.includes("_$callClinit")
|
|
||||||
) {
|
) {
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
@ -212,13 +204,14 @@ var main;(function(){`
|
|||||||
ModAPI.hooks.methods[\`${fullName}\`]=` +
|
ModAPI.hooks.methods[\`${fullName}\`]=` +
|
||||||
match.replace(fullName + "(", "(")
|
match.replace(fullName + "(", "(")
|
||||||
);
|
);
|
||||||
return match;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
var staticVariables = [
|
var staticVariables = [
|
||||||
...patchedFile.matchAll(/var \S+?_\S+?_\S+? = null;/gm),
|
...patchedFile.matchAll(/var \S+?_\S+?_\S+? = /gm),
|
||||||
].flatMap((x) => {
|
].flatMap((x) => {
|
||||||
return x[0];
|
return x[0];
|
||||||
|
}).filter(x => {
|
||||||
|
return (!x.includes("$_clinit_$")) && (!x.includes("$lambda$"))
|
||||||
});
|
});
|
||||||
patchedFile = patchedFile.replaceAll(
|
patchedFile = patchedFile.replaceAll(
|
||||||
/var \S+?_\S+? = \$rt_classWithoutFields\(\S*?\);/gm,
|
/var \S+?_\S+? = \$rt_classWithoutFields\(\S*?\);/gm,
|
||||||
@ -232,7 +225,7 @@ var main;(function(){`
|
|||||||
if (entry.startsWith(prefix)) {
|
if (entry.startsWith(prefix)) {
|
||||||
var variableName = entry
|
var variableName = entry
|
||||||
.replace("var ", "")
|
.replace("var ", "")
|
||||||
.replace(" = null;", "");
|
.replace(" = ", "");
|
||||||
var segments = variableName.split("_");
|
var segments = variableName.split("_");
|
||||||
segments.splice(0, 2);
|
segments.splice(0, 2);
|
||||||
var name = segments.join("_");
|
var name = segments.join("_");
|
||||||
@ -262,7 +255,7 @@ var main;(function(){`
|
|||||||
if (entry.startsWith(prefix)) {
|
if (entry.startsWith(prefix)) {
|
||||||
var variableName = entry
|
var variableName = entry
|
||||||
.replace("var ", "")
|
.replace("var ", "")
|
||||||
.replace(" = null;", "");
|
.replace(" = ", "");
|
||||||
var segments = variableName.split("_");
|
var segments = variableName.split("_");
|
||||||
segments.splice(0, 2);
|
segments.splice(0, 2);
|
||||||
var name = segments.join("_");
|
var name = segments.join("_");
|
||||||
@ -317,6 +310,7 @@ var main;(function(){`
|
|||||||
\<script id="modapi_postinit_data"\>globalThis.modapi_postinit = \`${globalThis.modapi_postinit}\`;\<\/script\>
|
\<script id="modapi_postinit_data"\>globalThis.modapi_postinit = \`${globalThis.modapi_postinit}\`;\<\/script\>
|
||||||
\<script id="libserverside"\>{"._|_libserverside_|_."}\<\/script\>`
|
\<script id="libserverside"\>{"._|_libserverside_|_."}\<\/script\>`
|
||||||
);
|
);
|
||||||
|
patchedFile = patchedFile.replace(`<title>EaglercraftX 1.8</title>`, `<title>EFI ${globalThis.ModAPIVersion}</title>`);
|
||||||
patchedFile = patchedFile.replaceAll(/main\(\);\s*?}/gm, (match) => {
|
patchedFile = patchedFile.replaceAll(/main\(\);\s*?}/gm, (match) => {
|
||||||
return match.replace("main();", "main();ModAPI.hooks._postInit();");
|
return match.replace("main();", "main();ModAPI.hooks._postInit();");
|
||||||
});
|
});
|
||||||
|
@ -36,18 +36,19 @@ async function shronk(input) {
|
|||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(inputHtml, 'text/html');
|
const doc = parser.parseFromString(inputHtml, 'text/html');
|
||||||
const scriptTags = doc.querySelectorAll('script');
|
const scriptTags = doc.querySelectorAll('script');
|
||||||
|
await wait(100); //trying to get chrome to gc
|
||||||
for (let i = 0; i < scriptTags.length; i++) {
|
for (let i = 0; i < scriptTags.length; i++) {
|
||||||
const scriptTag = scriptTags[i];
|
const scriptTag = scriptTags[i];
|
||||||
const code = scriptTag.textContent;
|
const code = scriptTag.textContent;
|
||||||
_status("[ASYNC_PLUGIN_1] Transpiling script #" + (i + 1) + " of length " + Math.round(code.length / 1000) + "k...");
|
_status("[ASYNC_PLUGIN_1] Transpiling script #" + (i + 1) + " of length " + Math.round(code.length / 1000) + "k...");
|
||||||
await wait(50);
|
await wait(150);
|
||||||
|
|
||||||
|
|
||||||
const output = Babel.transform(code, {
|
const output = Babel.transform(code, {
|
||||||
plugins: []
|
plugins: []
|
||||||
});
|
});
|
||||||
scriptTag.textContent = output.code;
|
scriptTag.textContent = output.code;
|
||||||
|
await wait(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
_status("[ASYNC_PLUGIN_1] Job complete!");
|
_status("[ASYNC_PLUGIN_1] Job complete!");
|
||||||
|
18
modgui.js
18
modgui.js
@ -24,7 +24,7 @@ globalThis.modapi_guikit = "(" + (() => {
|
|||||||
</h5>
|
</h5>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<table class="modTable">
|
<table id="modapi_gui_modTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
@ -162,15 +162,18 @@ globalThis.modapi_guikit = "(" + (() => {
|
|||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
box-shadow: 0 -4px 6px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 -4px 6px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
#modapi_gui_modTable {
|
||||||
|
min-width: 40vw;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
async function fileToDataURI(file) {
|
async function fileToText(file) {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
var fr = new FileReader();
|
var fr = new FileReader();
|
||||||
fr.addEventListener("error", (e) => { rej(e); });
|
fr.addEventListener("error", (e) => { rej(e); });
|
||||||
fr.addEventListener("load", (e) => { res(fr.result); });
|
fr.addEventListener("load", (e) => { res(fr.result); });
|
||||||
fr.readAsDataURL(file);
|
fr.readAsText(file);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
window.modapi_displayModGui = async function (cb) {
|
window.modapi_displayModGui = async function (cb) {
|
||||||
@ -191,7 +194,7 @@ globalThis.modapi_guikit = "(" + (() => {
|
|||||||
document.querySelector("#modapi_gui_container")._cb = cb;
|
document.querySelector("#modapi_gui_container")._cb = cb;
|
||||||
|
|
||||||
var modsList = await getMods();
|
var modsList = await getMods();
|
||||||
var tbody = document.querySelector("#modapi_gui_container .modTable tbody");
|
var tbody = document.querySelector("#modapi_gui_container #modapi_gui_modTable tbody");
|
||||||
tbody.innerHTML = "";
|
tbody.innerHTML = "";
|
||||||
modsList.forEach((modtxt, i) => {
|
modsList.forEach((modtxt, i) => {
|
||||||
if (!modtxt) { return }
|
if (!modtxt) { return }
|
||||||
@ -245,7 +248,6 @@ globalThis.modapi_guikit = "(" + (() => {
|
|||||||
var button = document.createElement("button");
|
var button = document.createElement("button");
|
||||||
button.innerText = "Delete";
|
button.innerText = "Delete";
|
||||||
button.style.height = "3rem";
|
button.style.height = "3rem";
|
||||||
button.style.marginTop = "calc(50% - 1.5rem)";
|
|
||||||
button.addEventListener("click", async () => {
|
button.addEventListener("click", async () => {
|
||||||
await removeMod(i);
|
await removeMod(i);
|
||||||
window.modapi_displayModGui();
|
window.modapi_displayModGui();
|
||||||
@ -254,7 +256,7 @@ globalThis.modapi_guikit = "(" + (() => {
|
|||||||
controls.appendChild(button);
|
controls.appendChild(button);
|
||||||
tr.appendChild(mod);
|
tr.appendChild(mod);
|
||||||
tr.appendChild(spacer);
|
tr.appendChild(spacer);
|
||||||
tr.appendChild(button);
|
tr.appendChild(controls);
|
||||||
tbody.appendChild(tr);
|
tbody.appendChild(tr);
|
||||||
});
|
});
|
||||||
var once = false;
|
var once = false;
|
||||||
@ -279,7 +281,7 @@ globalThis.modapi_guikit = "(" + (() => {
|
|||||||
if (!mod || mod.length === 0) {
|
if (!mod || mod.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await addMod("web@" + mod);
|
await addMod(mod);
|
||||||
window.modapi_displayModGui();
|
window.modapi_displayModGui();
|
||||||
}
|
}
|
||||||
window.modapi_uploadmod = async () => {
|
window.modapi_uploadmod = async () => {
|
||||||
@ -292,7 +294,7 @@ globalThis.modapi_guikit = "(" + (() => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < f.files.length; i++) {
|
for (let i = 0; i < f.files.length; i++) {
|
||||||
await addMod("web@" + (await fileToDataURI(f.files[i])).replaceAll(";base64", ";fs=" + f.files[i].name + ";base64"));
|
await addFileMod(f.files[i].name, (await fileToText(f.files[i])));
|
||||||
}
|
}
|
||||||
window.modapi_displayModGui();
|
window.modapi_displayModGui();
|
||||||
});
|
});
|
||||||
|
@ -1,212 +0,0 @@
|
|||||||
globalThis.modapi_modloader = `function promisifyIDBRequest(request) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
request.onsuccess = () => resolve(request.result);
|
|
||||||
request.onerror = () => reject(request.error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getDatabase() {
|
|
||||||
const dbRequest = indexedDB.open("EF_MODS");
|
|
||||||
const db = await promisifyIDBRequest(dbRequest);
|
|
||||||
|
|
||||||
if (!db.objectStoreNames.contains("filesystem")) {
|
|
||||||
db.close();
|
|
||||||
const version = db.version + 1;
|
|
||||||
const upgradeRequest = indexedDB.open("EF_MODS", version);
|
|
||||||
upgradeRequest.onupgradeneeded = (event) => {
|
|
||||||
const upgradedDb = event.target.result;
|
|
||||||
upgradedDb.createObjectStore("filesystem");
|
|
||||||
};
|
|
||||||
return promisifyIDBRequest(upgradeRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
return db;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getMods() {
|
|
||||||
const db = await getDatabase();
|
|
||||||
const transaction = db.transaction(["filesystem"], "readonly");
|
|
||||||
const objectStore = transaction.objectStore("filesystem");
|
|
||||||
const object = await promisifyIDBRequest(objectStore.get("mods.txt"));
|
|
||||||
return object ? (await object.text()).split("|") : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveMods(mods) {
|
|
||||||
const db = await getDatabase();
|
|
||||||
const transaction = db.transaction(["filesystem"], "readwrite");
|
|
||||||
const objectStore = transaction.objectStore("filesystem");
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
const modsData = encoder.encode(mods.join("|"));
|
|
||||||
const modsBlob = new Blob([modsData], { type: "text/plain" });
|
|
||||||
await promisifyIDBRequest(objectStore.put(modsBlob, "mods.txt"));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addMod(mod) {
|
|
||||||
const mods = await getMods();
|
|
||||||
mods.push(mod);
|
|
||||||
await saveMods(mods);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeMod(index) {
|
|
||||||
const mods = await getMods();
|
|
||||||
if (index >= 0 && index < mods.length) {
|
|
||||||
mods.splice(index, 1);
|
|
||||||
await saveMods(mods);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function resetMods() {
|
|
||||||
await saveMods([]);
|
|
||||||
console.log("Mods reset");
|
|
||||||
}
|
|
||||||
|
|
||||||
window.modLoader = async function modLoader(modsArr = []) {
|
|
||||||
if (!window.eaglerMLoaderMainRun) {
|
|
||||||
var searchParams = new URLSearchParams(location.search);
|
|
||||||
searchParams.getAll("mod").forEach((modToAdd) => {
|
|
||||||
console.log(
|
|
||||||
"[EaglerML] Adding mod to loadlist from search params: " + modToAdd
|
|
||||||
);
|
|
||||||
modsArr.push(modToAdd);
|
|
||||||
});
|
|
||||||
searchParams.getAll("plugin").forEach((modToAdd) => {
|
|
||||||
console.log(
|
|
||||||
"[EaglerML] Adding mod to loadlist from search params: " + modToAdd
|
|
||||||
);
|
|
||||||
modsArr.push(modToAdd);
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
!!eaglercraftXOpts &&
|
|
||||||
!!eaglercraftXOpts.Mods &&
|
|
||||||
Array.isArray(eaglercraftXOpts.Mods)
|
|
||||||
) {
|
|
||||||
eaglercraftXOpts.Mods.forEach((modToAdd) => {
|
|
||||||
console.log(
|
|
||||||
"[EaglerML] Adding mod to loadlist from eaglercraftXOpts: " +
|
|
||||||
modToAdd
|
|
||||||
);
|
|
||||||
modsArr.push(modToAdd);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("[EaglerML] Searching in iDB");
|
|
||||||
try {
|
|
||||||
var idbMods = await getMods();
|
|
||||||
modsArr = modsArr.concat(idbMods
|
|
||||||
.filter(x => { return x && x.length > 0 })
|
|
||||||
.flatMap(x => { if (x.startsWith("web@")) { return x.replace("web@", "") } return x })
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.eaglerMLoaderMainRun = true;
|
|
||||||
}
|
|
||||||
if (window.noLoadMods === true) {
|
|
||||||
modsArr.splice(0, modsArr.length);
|
|
||||||
}
|
|
||||||
function checkModsLoaded(totalLoaded, identifier) {
|
|
||||||
console.log(
|
|
||||||
"[EaglerML] Checking if mods are finished :: " +
|
|
||||||
totalLoaded +
|
|
||||||
"/" +
|
|
||||||
modsArr.length
|
|
||||||
);
|
|
||||||
if (totalLoaded >= modsArr.length) {
|
|
||||||
clearInterval(identifier);
|
|
||||||
window.ModGracePeriod = false;
|
|
||||||
if (
|
|
||||||
window.eaglerMLoaderMainRun &&
|
|
||||||
ModAPI &&
|
|
||||||
ModAPI.events &&
|
|
||||||
ModAPI.events.callEvent
|
|
||||||
) {
|
|
||||||
ModAPI.events.callEvent("load", {});
|
|
||||||
}
|
|
||||||
console.log(
|
|
||||||
"[EaglerML] Checking if mods are finished :: All mods loaded! Grace period off."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function methodB(currentMod) {
|
|
||||||
try {
|
|
||||||
console.log("[EaglerML] Loading " + currentMod + " via method B.");
|
|
||||||
var script = document.createElement("script");
|
|
||||||
script.setAttribute("data-hash", ModAPI.util.hashCode("web@"+currentMod));
|
|
||||||
script.src = currentMod;
|
|
||||||
script.setAttribute("data-isMod", "true");
|
|
||||||
script.onerror = () => {
|
|
||||||
console.log(
|
|
||||||
"[EaglerML] Failed to load " + currentMod + " via method B!"
|
|
||||||
);
|
|
||||||
script.remove();
|
|
||||||
totalLoaded++;
|
|
||||||
};
|
|
||||||
script.onload = () => {
|
|
||||||
console.log(
|
|
||||||
"[EaglerML] Successfully loaded " + currentMod + " via method B."
|
|
||||||
);
|
|
||||||
totalLoaded++;
|
|
||||||
};
|
|
||||||
document.body.appendChild(script);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(
|
|
||||||
"[EaglerML] Oh no! The mod " + currentMod + " failed to load!"
|
|
||||||
);
|
|
||||||
totalLoaded++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
window.ModGracePeriod = true;
|
|
||||||
var totalLoaded = 0;
|
|
||||||
var loaderCheckInterval = null;
|
|
||||||
modsArr.forEach((c) => {
|
|
||||||
let currentMod = c;
|
|
||||||
console.log("[EaglerML] Starting " + currentMod);
|
|
||||||
try {
|
|
||||||
var req = new XMLHttpRequest();
|
|
||||||
req.open("GET", currentMod);
|
|
||||||
req.onload = function xhrLoadHandler() {
|
|
||||||
console.log("[EaglerML] Loading " + currentMod + " via method A.");
|
|
||||||
var script = document.createElement("script");
|
|
||||||
script.setAttribute("data-hash", ModAPI.util.hashCode("web@"+currentMod));
|
|
||||||
try {
|
|
||||||
script.src =
|
|
||||||
"data:text/javascript," + encodeURIComponent(req.responseText);
|
|
||||||
} catch (error) {
|
|
||||||
methodB(currentMod);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
script.setAttribute("data-isMod", "true");
|
|
||||||
script.onerror = () => {
|
|
||||||
console.log(
|
|
||||||
"[EaglerML] Failed to load " + currentMod + " via method A!"
|
|
||||||
);
|
|
||||||
script.remove();
|
|
||||||
totalLoaded++;
|
|
||||||
};
|
|
||||||
script.onload = () => {
|
|
||||||
console.log(
|
|
||||||
"[EaglerML] Successfully loaded " + currentMod + " via method A."
|
|
||||||
);
|
|
||||||
totalLoaded++;
|
|
||||||
};
|
|
||||||
document.body.appendChild(script);
|
|
||||||
};
|
|
||||||
req.onerror = function xhrErrorHandler() {
|
|
||||||
methodB(currentMod);
|
|
||||||
};
|
|
||||||
req.send();
|
|
||||||
} catch (error) {
|
|
||||||
methodB(currentMod);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
loaderCheckInterval = setInterval(() => {
|
|
||||||
checkModsLoaded(totalLoaded, loaderCheckInterval);
|
|
||||||
}, 500);
|
|
||||||
console.log(
|
|
||||||
"[EaglerML] Starting to load " + modsArr.length + " mods..."
|
|
||||||
);
|
|
||||||
window.returnTotalLoadedMods = function returnTotalLoadedMods() {
|
|
||||||
return totalLoaded;
|
|
||||||
};
|
|
||||||
};`;
|
|
95
modloader.js
95
modloader.js
@ -1,11 +1,12 @@
|
|||||||
function promisifyIDBRequest(request) {
|
globalThis.modapi_modloader = "(" + (() => {
|
||||||
|
globalThis.promisifyIDBRequest = function promisifyIDBRequest(request) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
request.onsuccess = () => resolve(request.result);
|
request.onsuccess = () => resolve(request.result);
|
||||||
request.onerror = () => reject(request.error);
|
request.onerror = () => reject(request.error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getDatabase() {
|
globalThis.getDatabase = async function getDatabase() {
|
||||||
const dbRequest = indexedDB.open("EF_MODS");
|
const dbRequest = indexedDB.open("EF_MODS");
|
||||||
const db = await promisifyIDBRequest(dbRequest);
|
const db = await promisifyIDBRequest(dbRequest);
|
||||||
|
|
||||||
@ -23,57 +24,97 @@ async function getDatabase() {
|
|||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMods() {
|
globalThis.getMods = async function getMods() {
|
||||||
const db = await getDatabase();
|
const db = await getDatabase();
|
||||||
const transaction = db.transaction(["filesystem"], "readonly");
|
const transaction = db.transaction(["filesystem"], "readonly");
|
||||||
const objectStore = transaction.objectStore("filesystem");
|
const objectStore = transaction.objectStore("filesystem");
|
||||||
const object = await promisifyIDBRequest(objectStore.get("mods.txt"));
|
const object = await promisifyIDBRequest(objectStore.get("mods.txt"));
|
||||||
return object ? (await object.text()).split("|") : [];
|
var out = object ? (await object.text()).split("|").toSorted() : [];
|
||||||
|
db.close();
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveMods(mods) {
|
globalThis.getMod = async function getMod(mod) {
|
||||||
|
const db = await getDatabase();
|
||||||
|
const transaction = db.transaction(["filesystem"], "readonly");
|
||||||
|
const objectStore = transaction.objectStore("filesystem");
|
||||||
|
const object = await promisifyIDBRequest(objectStore.get("mods/" + mod));
|
||||||
|
var out = object ? (await object.text()) : "";
|
||||||
|
db.close();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.saveMods = async function saveMods(mods) {
|
||||||
const db = await getDatabase();
|
const db = await getDatabase();
|
||||||
const transaction = db.transaction(["filesystem"], "readwrite");
|
const transaction = db.transaction(["filesystem"], "readwrite");
|
||||||
const objectStore = transaction.objectStore("filesystem");
|
const objectStore = transaction.objectStore("filesystem");
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const modsData = encoder.encode(mods.join("|"));
|
const modsData = encoder.encode(mods.toSorted().join("|"));
|
||||||
const modsBlob = new Blob([modsData], { type: "text/plain" });
|
const modsBlob = new Blob([modsData], { type: "text/plain" });
|
||||||
await promisifyIDBRequest(objectStore.put(modsBlob, "mods.txt"));
|
await promisifyIDBRequest(objectStore.put(modsBlob, "mods.txt"));
|
||||||
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addMod(mod) {
|
globalThis.addMod = async function addMod(mod) {
|
||||||
|
const mods = await getMods();
|
||||||
|
mods.push("web@" + mod);
|
||||||
|
await saveMods(mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.addFileMod = async function addFileMod(mod, textContents) {
|
||||||
const mods = await getMods();
|
const mods = await getMods();
|
||||||
mods.push(mod);
|
mods.push(mod);
|
||||||
await saveMods(mods);
|
await saveMods(mods);
|
||||||
|
|
||||||
|
const db = await getDatabase();
|
||||||
|
const transaction = db.transaction(["filesystem"], "readwrite");
|
||||||
|
const objectStore = transaction.objectStore("filesystem");
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const modsData = encoder.encode(textContents);
|
||||||
|
const modsBlob = new Blob([modsData], { type: "text/plain" });
|
||||||
|
await promisifyIDBRequest(objectStore.put(modsBlob, "mods/" + mod));
|
||||||
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeMod(index) {
|
globalThis.removeMod = async function removeMod(index) {
|
||||||
const mods = await getMods();
|
const mods = await getMods();
|
||||||
if (index >= 0 && index < mods.length) {
|
if (index >= 0 && index < mods.length) {
|
||||||
mods.splice(index, 1);
|
var deleted = mods.splice(index, 1)[0];
|
||||||
await saveMods(mods);
|
await saveMods(mods);
|
||||||
|
if (!deleted.startsWith("web@")) {
|
||||||
|
const db = await getDatabase();
|
||||||
|
const transaction = db.transaction(["filesystem"], "readwrite");
|
||||||
|
const objectStore = transaction.objectStore("filesystem");
|
||||||
|
await promisifyIDBRequest(objectStore.delete("mods/" + deleted));
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resetMods() {
|
globalThis.resetMods = async function resetMods() {
|
||||||
await saveMods([]);
|
console.log("Resetting mods...");
|
||||||
|
const db = await getDatabase();
|
||||||
|
const transaction = db.transaction(["filesystem"], "readwrite");
|
||||||
|
const objectStore = transaction.objectStore("filesystem");
|
||||||
|
await promisifyIDBRequest(objectStore.clear());
|
||||||
console.log("Mods reset");
|
console.log("Mods reset");
|
||||||
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.modLoader = async function modLoader(modsArr = []) {
|
globalThis.modLoader = async function modLoader(modsArr = []) {
|
||||||
if (!window.eaglerMLoaderMainRun) {
|
if (!window.eaglerMLoaderMainRun) {
|
||||||
var searchParams = new URLSearchParams(location.search);
|
var searchParams = new URLSearchParams(location.search);
|
||||||
searchParams.getAll("mod").forEach((modToAdd) => {
|
searchParams.getAll("mod").forEach((modToAdd) => {
|
||||||
console.log(
|
console.log(
|
||||||
"[EaglerML] Adding mod to loadlist from search params: " + modToAdd
|
"[EaglerML] Adding mod to loadlist from search params: " + modToAdd
|
||||||
);
|
);
|
||||||
modsArr.push(modToAdd);
|
modsArr.push("web@" + modToAdd);
|
||||||
});
|
});
|
||||||
searchParams.getAll("plugin").forEach((modToAdd) => {
|
searchParams.getAll("plugin").forEach((modToAdd) => {
|
||||||
console.log(
|
console.log(
|
||||||
"[EaglerML] Adding mod to loadlist from search params: " + modToAdd
|
"[EaglerML] Adding mod to loadlist from search params: " + modToAdd
|
||||||
);
|
);
|
||||||
modsArr.push(modToAdd);
|
modsArr.push("web@" + modToAdd);
|
||||||
});
|
});
|
||||||
if (
|
if (
|
||||||
!!eaglercraftXOpts &&
|
!!eaglercraftXOpts &&
|
||||||
@ -94,7 +135,6 @@ window.modLoader = async function modLoader(modsArr = []) {
|
|||||||
var idbMods = await getMods();
|
var idbMods = await getMods();
|
||||||
modsArr = modsArr.concat(idbMods
|
modsArr = modsArr.concat(idbMods
|
||||||
.filter(x => { return x && x.length > 0 })
|
.filter(x => { return x && x.length > 0 })
|
||||||
.flatMap(x => { if (x.startsWith("web@")) { return x.replace("web@", "") } return x })
|
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -159,19 +199,22 @@ window.modLoader = async function modLoader(modsArr = []) {
|
|||||||
window.ModGracePeriod = true;
|
window.ModGracePeriod = true;
|
||||||
var totalLoaded = 0;
|
var totalLoaded = 0;
|
||||||
var loaderCheckInterval = null;
|
var loaderCheckInterval = null;
|
||||||
modsArr.forEach((c) => {
|
modsArr.sort();
|
||||||
let currentMod = c;
|
for (let i = 0; i < modsArr.length; i++) {
|
||||||
|
let currentMod = modsArr[i];
|
||||||
|
var isIDBMod = !currentMod.startsWith("web@");
|
||||||
|
if (!isIDBMod) {
|
||||||
|
currentMod = currentMod.replace("web@", "");
|
||||||
|
}
|
||||||
console.log("[EaglerML] Starting " + currentMod);
|
console.log("[EaglerML] Starting " + currentMod);
|
||||||
try {
|
try {
|
||||||
var req = new XMLHttpRequest();
|
var responseText = isIDBMod ? await getMod(currentMod) : await (await fetch(currentMod)).text();
|
||||||
req.open("GET", currentMod);
|
|
||||||
req.onload = function xhrLoadHandler() {
|
|
||||||
console.log("[EaglerML] Loading " + currentMod + " via method A.");
|
console.log("[EaglerML] Loading " + currentMod + " via method A.");
|
||||||
var script = document.createElement("script");
|
var script = document.createElement("script");
|
||||||
script.setAttribute("data-hash", ModAPI.util.hashCode("web@"+currentMod));
|
script.setAttribute("data-hash", ModAPI.util.hashCode((isIDBMod ? "" : "web@") + currentMod));
|
||||||
try {
|
try {
|
||||||
script.src =
|
script.src =
|
||||||
"data:text/javascript," + encodeURIComponent(req.responseText);
|
"data:text/javascript," + encodeURIComponent(responseText);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
methodB(currentMod);
|
methodB(currentMod);
|
||||||
return;
|
return;
|
||||||
@ -191,15 +234,10 @@ window.modLoader = async function modLoader(modsArr = []) {
|
|||||||
totalLoaded++;
|
totalLoaded++;
|
||||||
};
|
};
|
||||||
document.body.appendChild(script);
|
document.body.appendChild(script);
|
||||||
};
|
|
||||||
req.onerror = function xhrErrorHandler() {
|
|
||||||
methodB(currentMod);
|
|
||||||
};
|
|
||||||
req.send();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
methodB(currentMod);
|
methodB(currentMod);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
loaderCheckInterval = setInterval(() => {
|
loaderCheckInterval = setInterval(() => {
|
||||||
checkModsLoaded(totalLoaded, loaderCheckInterval);
|
checkModsLoaded(totalLoaded, loaderCheckInterval);
|
||||||
}, 500);
|
}, 500);
|
||||||
@ -210,3 +248,4 @@ window.modLoader = async function modLoader(modsArr = []) {
|
|||||||
return totalLoaded;
|
return totalLoaded;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
}).toString() + ")();"
|
17
postinit.js
17
postinit.js
@ -1,3 +1,4 @@
|
|||||||
|
globalThis.ModAPIVersion = "v2.3.3";
|
||||||
globalThis.modapi_postinit = "(" + (() => {
|
globalThis.modapi_postinit = "(" + (() => {
|
||||||
//EaglerForge post initialization code.
|
//EaglerForge post initialization code.
|
||||||
//This script cannot contain backticks, escape characters, or backslashes in order to inject into the dedicated server code.
|
//This script cannot contain backticks, escape characters, or backslashes in order to inject into the dedicated server code.
|
||||||
@ -22,7 +23,7 @@ globalThis.modapi_postinit = "(" + (() => {
|
|||||||
ModAPI.meta._versionMap = {};
|
ModAPI.meta._versionMap = {};
|
||||||
ModAPI.array = {};
|
ModAPI.array = {};
|
||||||
|
|
||||||
ModAPI.version = "v2.3.1";
|
ModAPI.version = "__modapi_version_code__";
|
||||||
ModAPI.flavour = "injector";
|
ModAPI.flavour = "injector";
|
||||||
ModAPI.GNU = "terry pratchett";
|
ModAPI.GNU = "terry pratchett";
|
||||||
ModAPI.credits = ["ZXMushroom63", "radmanplays", "Murturtle", "OtterCodes101", "TheIdiotPlays", "OeildeLynx31", "Stpv22"];
|
ModAPI.credits = ["ZXMushroom63", "radmanplays", "Murturtle", "OtterCodes101", "TheIdiotPlays", "OeildeLynx31", "Stpv22"];
|
||||||
@ -889,12 +890,16 @@ globalThis.modapi_postinit = "(" + (() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ModAPI.events.newEvent("bootstrap", "server");
|
ModAPI.events.newEvent("bootstrap", "server");
|
||||||
|
const bootstrapClass = ModAPI.reflect.getClassById("net.minecraft.init.Bootstrap");
|
||||||
const originalBootstrap = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.init.Bootstrap", "register")];
|
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) {
|
ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.init.Bootstrap", "register")] = function (...args) {
|
||||||
|
if (bootstrapClass.staticVariables.alreadyRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var x = originalBootstrap.apply(this, args);
|
var x = originalBootstrap.apply(this, args);
|
||||||
ModAPI.util.bootstrap();
|
ModAPI.util.bootstrap();
|
||||||
ModAPI.events.callEvent("bootstrap", {});
|
|
||||||
console.log("[ModAPI] Hooked into bootstrap. .blocks, .items, .materials and .enchantments are now accessible.");
|
console.log("[ModAPI] Hooked into bootstrap. .blocks, .items, .materials and .enchantments are now accessible.");
|
||||||
|
ModAPI.events.callEvent("bootstrap", {});
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -954,8 +959,8 @@ globalThis.modapi_postinit = "(" + (() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function qhash(txt, arr) {
|
function qhash(txt, arr) {
|
||||||
var interval = 4095 - arr.length;
|
var interval = 4095; //used to be 4095 - arr.length, but that increases incompatibility based on load order and otehr circumstances
|
||||||
if (interval < 1) {
|
if (arr.length >= 4095) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
@ -968,7 +973,7 @@ globalThis.modapi_postinit = "(" + (() => {
|
|||||||
}
|
}
|
||||||
var hash = x;
|
var hash = x;
|
||||||
while (arr.includes(hash)) {
|
while (arr.includes(hash)) {
|
||||||
hash = (hash + 1) % (interval + arr.length);
|
hash = (hash + 1) % interval;
|
||||||
}
|
}
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
@ -989,4 +994,4 @@ globalThis.modapi_postinit = "(" + (() => {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
}).toString() + ")();";
|
}).toString().replace("__modapi_version_code__", ModAPIVersion) + ")();";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user