mirror of
https://github.com/eaglerforge/EaglerForgeInjector
synced 2025-07-25 07:01:20 -09:00
commit
bc0bba9bde
@ -1,5 +1,10 @@
|
||||
# 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:
|
||||
#### 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)
|
||||
- [Slippery Mod](slippery.md)
|
||||
- [/hat mod](hat.md)
|
||||
- [/spawnxp command](comingsoon)
|
||||
- [/spawnxp command](spawnxp.md)
|
||||
|
||||
### Advanced
|
||||
Prerequisites:
|
||||
@ -33,6 +33,6 @@ Prerequisites:
|
||||
- Your EaglerForgeInjector processed.html opened in an editor (optional)
|
||||
|
||||
Tutorials:
|
||||
- [Custom Blocks](comingsoon)
|
||||
- [Custom Blocks](custom_block.md)
|
||||
- [Custom Items](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() {
|
||||
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 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());
|
||||
this.$defaultBlockState = this.$blockState.$getBaseState();
|
||||
this.$setCreativeTab(creativeBlockTab);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 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 creativeBlockTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabBlock;
|
||||
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.
|
||||
this.$defaultBlockState = this.$blockState.$getBaseState();
|
||||
this.$setCreativeTab(creativeBlockTab);
|
||||
}
|
||||
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) {
|
||||
var world = ModAPI.util.wrap($world);
|
||||
var blockpos = ModAPI.util.wrap($blockpos);
|
||||
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,
|
||||
blockpos.getZ() + 0.5, 9, 1, 1);
|
||||
blockpos.getZ() + 0.5, 9, 1, 0);
|
||||
}
|
||||
return breakBlockMethod(this, $world, $blockpos, $blockstate);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
var example_item = (new nmi_ItemExample()).$setUnlocalizedName(
|
||||
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;
|
||||
|
||||
return example_item;
|
||||
|
@ -117,7 +117,7 @@
|
||||
<summary>
|
||||
How do I compile my own unobfuscated unsigned Eaglercraft build?
|
||||
</summary>
|
||||
<a href="docs/compiling_client.md">tutorial here</a>
|
||||
<a href="docs/compiling_client">tutorial here</a>
|
||||
</details>
|
||||
<details>
|
||||
<summary>How does this tool work?</summary>
|
||||
@ -169,7 +169,7 @@
|
||||
|
||||
<!-- Code assets -->
|
||||
<script src="postinit.js"></script>
|
||||
<script src="modloader.injector.js"></script>
|
||||
<script src="modloader.js"></script>
|
||||
<script src="modgui.js"></script>
|
||||
<script src="efserver.js"></script>
|
||||
</body>
|
||||
|
28
injector.js
28
injector.js
@ -18,24 +18,17 @@ function entriesToStaticVariableProxy(entries, prefix) {
|
||||
var getComponents = "";
|
||||
entries.forEach((entry) => {
|
||||
getComponents += `
|
||||
case \`${entry.name}\`:
|
||||
return ${entry.variable};
|
||||
break;`;
|
||||
case \`${entry.name}\`: return ${entry.variable};`;
|
||||
});
|
||||
getComponents += `
|
||||
default:
|
||||
return Reflect.get(a,b,c);`
|
||||
default: return Reflect.get(a,b,c);`
|
||||
|
||||
var setComponents = "";
|
||||
entries.forEach((entry) => {
|
||||
setComponents += `
|
||||
case \`${entry.name}\`:
|
||||
${entry.variable} = c;
|
||||
break;`;
|
||||
case \`${entry.name}\`: ${entry.variable} = c; break;`;
|
||||
});
|
||||
setComponents += `
|
||||
default:
|
||||
a[b]=c;`
|
||||
setComponents += ` default: a[b]=c;`
|
||||
/*/
|
||||
|
||||
ModAPI.hooks._rippedStaticIndexer[\`${prefix.replace(
|
||||
@ -196,8 +189,7 @@ var main;(function(){`
|
||||
(match) => {
|
||||
if (
|
||||
match.includes("__init_") ||
|
||||
match.includes("__clinit_") ||
|
||||
match.includes("_$callClinit")
|
||||
match.includes("__clinit_")
|
||||
) {
|
||||
return match;
|
||||
}
|
||||
@ -212,13 +204,14 @@ var main;(function(){`
|
||||
ModAPI.hooks.methods[\`${fullName}\`]=` +
|
||||
match.replace(fullName + "(", "(")
|
||||
);
|
||||
return match;
|
||||
}
|
||||
);
|
||||
var staticVariables = [
|
||||
...patchedFile.matchAll(/var \S+?_\S+?_\S+? = null;/gm),
|
||||
...patchedFile.matchAll(/var \S+?_\S+?_\S+? = /gm),
|
||||
].flatMap((x) => {
|
||||
return x[0];
|
||||
}).filter(x => {
|
||||
return (!x.includes("$_clinit_$")) && (!x.includes("$lambda$"))
|
||||
});
|
||||
patchedFile = patchedFile.replaceAll(
|
||||
/var \S+?_\S+? = \$rt_classWithoutFields\(\S*?\);/gm,
|
||||
@ -232,7 +225,7 @@ var main;(function(){`
|
||||
if (entry.startsWith(prefix)) {
|
||||
var variableName = entry
|
||||
.replace("var ", "")
|
||||
.replace(" = null;", "");
|
||||
.replace(" = ", "");
|
||||
var segments = variableName.split("_");
|
||||
segments.splice(0, 2);
|
||||
var name = segments.join("_");
|
||||
@ -262,7 +255,7 @@ var main;(function(){`
|
||||
if (entry.startsWith(prefix)) {
|
||||
var variableName = entry
|
||||
.replace("var ", "")
|
||||
.replace(" = null;", "");
|
||||
.replace(" = ", "");
|
||||
var segments = variableName.split("_");
|
||||
segments.splice(0, 2);
|
||||
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="libserverside"\>{"._|_libserverside_|_."}\<\/script\>`
|
||||
);
|
||||
patchedFile = patchedFile.replace(`<title>EaglercraftX 1.8</title>`, `<title>EFI ${globalThis.ModAPIVersion}</title>`);
|
||||
patchedFile = patchedFile.replaceAll(/main\(\);\s*?}/gm, (match) => {
|
||||
return match.replace("main();", "main();ModAPI.hooks._postInit();");
|
||||
});
|
||||
|
@ -36,18 +36,19 @@ async function shronk(input) {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(inputHtml, 'text/html');
|
||||
const scriptTags = doc.querySelectorAll('script');
|
||||
|
||||
await wait(100); //trying to get chrome to gc
|
||||
for (let i = 0; i < scriptTags.length; i++) {
|
||||
const scriptTag = scriptTags[i];
|
||||
const code = scriptTag.textContent;
|
||||
_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, {
|
||||
plugins: []
|
||||
});
|
||||
scriptTag.textContent = output.code;
|
||||
await wait(10);
|
||||
}
|
||||
|
||||
_status("[ASYNC_PLUGIN_1] Job complete!");
|
||||
|
18
modgui.js
18
modgui.js
@ -24,7 +24,7 @@ globalThis.modapi_guikit = "(" + (() => {
|
||||
</h5>
|
||||
</header>
|
||||
|
||||
<table class="modTable">
|
||||
<table id="modapi_gui_modTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
@ -162,15 +162,18 @@ globalThis.modapi_guikit = "(" + (() => {
|
||||
font-size: 1.25rem;
|
||||
box-shadow: 0 -4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
#modapi_gui_modTable {
|
||||
min-width: 40vw;
|
||||
}
|
||||
</style>
|
||||
</div>`;
|
||||
|
||||
async function fileToDataURI(file) {
|
||||
async function fileToText(file) {
|
||||
return new Promise((res, rej) => {
|
||||
var fr = new FileReader();
|
||||
fr.addEventListener("error", (e) => { rej(e); });
|
||||
fr.addEventListener("load", (e) => { res(fr.result); });
|
||||
fr.readAsDataURL(file);
|
||||
fr.readAsText(file);
|
||||
});
|
||||
}
|
||||
window.modapi_displayModGui = async function (cb) {
|
||||
@ -191,7 +194,7 @@ globalThis.modapi_guikit = "(" + (() => {
|
||||
document.querySelector("#modapi_gui_container")._cb = cb;
|
||||
|
||||
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 = "";
|
||||
modsList.forEach((modtxt, i) => {
|
||||
if (!modtxt) { return }
|
||||
@ -245,7 +248,6 @@ globalThis.modapi_guikit = "(" + (() => {
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "Delete";
|
||||
button.style.height = "3rem";
|
||||
button.style.marginTop = "calc(50% - 1.5rem)";
|
||||
button.addEventListener("click", async () => {
|
||||
await removeMod(i);
|
||||
window.modapi_displayModGui();
|
||||
@ -254,7 +256,7 @@ globalThis.modapi_guikit = "(" + (() => {
|
||||
controls.appendChild(button);
|
||||
tr.appendChild(mod);
|
||||
tr.appendChild(spacer);
|
||||
tr.appendChild(button);
|
||||
tr.appendChild(controls);
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
var once = false;
|
||||
@ -279,7 +281,7 @@ globalThis.modapi_guikit = "(" + (() => {
|
||||
if (!mod || mod.length === 0) {
|
||||
return;
|
||||
}
|
||||
await addMod("web@" + mod);
|
||||
await addMod(mod);
|
||||
window.modapi_displayModGui();
|
||||
}
|
||||
window.modapi_uploadmod = async () => {
|
||||
@ -292,7 +294,7 @@ globalThis.modapi_guikit = "(" + (() => {
|
||||
return;
|
||||
}
|
||||
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();
|
||||
});
|
||||
|
@ -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;
|
||||
};
|
||||
};`;
|
381
modloader.js
381
modloader.js
@ -1,177 +1,220 @@
|
||||
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);
|
||||
globalThis.modapi_modloader = "(" + (() => {
|
||||
globalThis.promisifyIDBRequest = function promisifyIDBRequest(request) {
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
globalThis.getDatabase = async function getDatabase() {
|
||||
const dbRequest = indexedDB.open("EF_MODS");
|
||||
const db = await promisifyIDBRequest(dbRequest);
|
||||
|
||||
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("|") : [];
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
async function addMod(mod) {
|
||||
const mods = await getMods();
|
||||
mods.push(mod);
|
||||
await saveMods(mods);
|
||||
}
|
||||
globalThis.getMods = 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"));
|
||||
var out = object ? (await object.text()).split("|").toSorted() : [];
|
||||
db.close();
|
||||
return out;
|
||||
}
|
||||
|
||||
async function removeMod(index) {
|
||||
const mods = await getMods();
|
||||
if (index >= 0 && index < mods.length) {
|
||||
mods.splice(index, 1);
|
||||
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 transaction = db.transaction(["filesystem"], "readwrite");
|
||||
const objectStore = transaction.objectStore("filesystem");
|
||||
const encoder = new TextEncoder();
|
||||
const modsData = encoder.encode(mods.toSorted().join("|"));
|
||||
const modsBlob = new Blob([modsData], { type: "text/plain" });
|
||||
await promisifyIDBRequest(objectStore.put(modsBlob, "mods.txt"));
|
||||
db.close();
|
||||
}
|
||||
|
||||
globalThis.addMod = async function addMod(mod) {
|
||||
const mods = await getMods();
|
||||
mods.push("web@" + mod);
|
||||
await saveMods(mods);
|
||||
}
|
||||
}
|
||||
|
||||
async function resetMods() {
|
||||
await saveMods([]);
|
||||
console.log("Mods reset");
|
||||
}
|
||||
globalThis.addFileMod = async function addFileMod(mod, textContents) {
|
||||
const mods = await getMods();
|
||||
mods.push(mod);
|
||||
await saveMods(mods);
|
||||
|
||||
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;
|
||||
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();
|
||||
}
|
||||
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", {});
|
||||
|
||||
globalThis.removeMod = async function removeMod(index) {
|
||||
const mods = await getMods();
|
||||
if (index >= 0 && index < mods.length) {
|
||||
var deleted = mods.splice(index, 1)[0];
|
||||
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();
|
||||
}
|
||||
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++;
|
||||
}
|
||||
|
||||
globalThis.resetMods = async function resetMods() {
|
||||
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");
|
||||
db.close();
|
||||
}
|
||||
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() {
|
||||
|
||||
globalThis.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("web@" + modToAdd);
|
||||
});
|
||||
searchParams.getAll("plugin").forEach((modToAdd) => {
|
||||
console.log(
|
||||
"[EaglerML] Adding mod to loadlist from search params: " + modToAdd
|
||||
);
|
||||
modsArr.push("web@" + 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 })
|
||||
);
|
||||
} 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.sort();
|
||||
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);
|
||||
try {
|
||||
var responseText = isIDBMod ? await getMod(currentMod) : await (await fetch(currentMod)).text();
|
||||
console.log("[EaglerML] Loading " + currentMod + " via method A.");
|
||||
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 {
|
||||
script.src =
|
||||
"data:text/javascript," + encodeURIComponent(req.responseText);
|
||||
"data:text/javascript," + encodeURIComponent(responseText);
|
||||
} catch (error) {
|
||||
methodB(currentMod);
|
||||
return;
|
||||
@ -191,22 +234,18 @@ window.modLoader = async function modLoader(modsArr = []) {
|
||||
totalLoaded++;
|
||||
};
|
||||
document.body.appendChild(script);
|
||||
};
|
||||
req.onerror = function xhrErrorHandler() {
|
||||
} catch (error) {
|
||||
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;
|
||||
loaderCheckInterval = setInterval(() => {
|
||||
checkModsLoaded(totalLoaded, loaderCheckInterval);
|
||||
}, 500);
|
||||
console.log(
|
||||
"[EaglerML] Starting to load " + modsArr.length + " mods..."
|
||||
);
|
||||
window.returnTotalLoadedMods = function returnTotalLoadedMods() {
|
||||
return totalLoaded;
|
||||
};
|
||||
};
|
||||
};
|
||||
}).toString() + ")();"
|
17
postinit.js
17
postinit.js
@ -1,3 +1,4 @@
|
||||
globalThis.ModAPIVersion = "v2.3.3";
|
||||
globalThis.modapi_postinit = "(" + (() => {
|
||||
//EaglerForge post initialization 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.array = {};
|
||||
|
||||
ModAPI.version = "v2.3.1";
|
||||
ModAPI.version = "__modapi_version_code__";
|
||||
ModAPI.flavour = "injector";
|
||||
ModAPI.GNU = "terry pratchett";
|
||||
ModAPI.credits = ["ZXMushroom63", "radmanplays", "Murturtle", "OtterCodes101", "TheIdiotPlays", "OeildeLynx31", "Stpv22"];
|
||||
@ -889,12 +890,16 @@ globalThis.modapi_postinit = "(" + (() => {
|
||||
}
|
||||
|
||||
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")];
|
||||
ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.init.Bootstrap", "register")] = function (...args) {
|
||||
if (bootstrapClass.staticVariables.alreadyRegistered) {
|
||||
return;
|
||||
}
|
||||
var x = originalBootstrap.apply(this, args);
|
||||
ModAPI.util.bootstrap();
|
||||
ModAPI.events.callEvent("bootstrap", {});
|
||||
console.log("[ModAPI] Hooked into bootstrap. .blocks, .items, .materials and .enchantments are now accessible.");
|
||||
ModAPI.events.callEvent("bootstrap", {});
|
||||
return x;
|
||||
}
|
||||
|
||||
@ -954,8 +959,8 @@ globalThis.modapi_postinit = "(" + (() => {
|
||||
}
|
||||
|
||||
function qhash(txt, arr) {
|
||||
var interval = 4095 - arr.length;
|
||||
if (interval < 1) {
|
||||
var interval = 4095; //used to be 4095 - arr.length, but that increases incompatibility based on load order and otehr circumstances
|
||||
if (arr.length >= 4095) {
|
||||
console.error("[ModAPI.keygen] Ran out of IDs while generating for " + txt);
|
||||
return -1;
|
||||
}
|
||||
@ -968,7 +973,7 @@ globalThis.modapi_postinit = "(" + (() => {
|
||||
}
|
||||
var hash = x;
|
||||
while (arr.includes(hash)) {
|
||||
hash = (hash + 1) % (interval + arr.length);
|
||||
hash = (hash + 1) % interval;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
@ -989,4 +994,4 @@ globalThis.modapi_postinit = "(" + (() => {
|
||||
var values = [...ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry.$modapi_specmap.values()];
|
||||
return qhash(block, values);
|
||||
}
|
||||
}).toString() + ")();";
|
||||
}).toString().replace("__modapi_version_code__", ModAPIVersion) + ")();";
|
||||
|
Loading…
x
Reference in New Issue
Block a user