Merge pull request #111 from eaglerforge/main

v2.7.93
This commit is contained in:
ZXMushroom63 2025-06-13 17:30:21 +08:00 committed by GitHub
commit e09077fbeb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 342 additions and 32 deletions

View File

@ -24,7 +24,7 @@ Download this repository as a .zip, and extract it. Open index.html with your pr
- Run `npx efi /help` for use instructions.
#### How does it work?
This tool matches patterns in eaglercraft builds and adds patching code to let you modify how the code works at runtime. It then adds a [corelib](./postinit.js) that initialises the `ModAPI` object.
This tool matches patterns in eaglercraft builds and adds patching code to let you modify how the code works at runtime. It then adds a [corelib](./core/postinit.js) that initialises the `ModAPI` object.
#### History
EaglerForgeInjector is a replacement for the `ModAPI` in the [old eaglerforge](https://github.com/EaglerForge/EaglerForge-old), which was maintained by @radmanplays. The legacy eaglerforge was a port of [Leah Anderson's EaglerReborn (dmca'd)](https://github.com/EaglerReborn/reborn)'s `PluginAPI` (created by me, @ZXMushroom63) to run on newer versions of Eaglercraft, with a few improvements and new features. Unlike EaglerForgeInjector, both legacy eaglerforge and eaglerreborn manually exposed properties and methods one by one.

View File

@ -37,7 +37,7 @@ var modapi_preinit = `globalThis.ModAPI ||= {};
`;
var freezeCallstack = `if(ModAPI.hooks.freezeCallstack){return false};`;
const EFIConfig = {
ModAPIVersion: "v2.7.92", //also change in package.json
ModAPIVersion: "v2.7.93", //also change in package.json
doEaglerforge: true,
verbose: false,
doServerExtras: false,

View File

@ -27,6 +27,7 @@ const modapi_postinit = "(" + (() => {
ModAPI.meta._developerMap = {};
ModAPI.meta._iconMap = {};
ModAPI.meta._versionMap = {};
ModAPI.isServer = false;
const credits = {};
ModAPI.addCredit = function (category, name, contents) {
if (!credits[category]) {
@ -36,7 +37,7 @@ const modapi_postinit = "(" + (() => {
}
function getCreditsString() {
return Object.entries(credits).map((entry) => {
return " "+entry[0] + LF + " " + (new Array(entry[0].length)).fill("~").join("") + entry[1].join("") + LF + LF + LF;
return " " + entry[0] + LF + " " + (new Array(entry[0].length)).fill("~").join("") + entry[1].join("") + LF + LF + LF;
}).join("");
}
ModAPI.array = {};
@ -140,6 +141,9 @@ const modapi_postinit = "(" + (() => {
}
});
}
function easyStaticPropAlias(clsId, real, alias) {
easyAlias(ModAPI.reflect.getClassById(clsId).staticVariables, real, alias);
}
ModAPI.meta.title = function (title) {
if (!document.currentScript || document.currentScript.getAttribute("data-isMod") !== "true") {
return console.log("[ModAPIMeta] Cannot set meta for non-mod script.");
@ -216,6 +220,7 @@ const modapi_postinit = "(" + (() => {
ModAPI.util.getMethodFromPackage = function (classId, methodName) {
if (ModAPI.is_1_12) {
classId = classId.replace(".eaglercraft.v1_8", ".eaglercraft"); //why peyton why must you do this. you couldve changed it to v1_12 too, that would've worked
classId = classId.replace(".entity.RenderItem", ".RenderItem");
}
var name = "";
var classStuff = classId.split(".");
@ -571,6 +576,24 @@ const modapi_postinit = "(" + (() => {
return target;
}
}
if (prop === "getCorrective") {
return function () {
return new Proxy(target, patchProxyConfToCorrective(TeaVMArray_To_Recursive_BaseData_ProxyConf));
}
}
if (prop === "isCorrective") {
return function () {
return corrective;
}
}
if (prop === "reload") {
return function () {
if (reloadDeprecationWarnings < 10) {
console.warn("ModAPI/PluginAPI reload() is obsolete, please stop using it in code.")
reloadDeprecationWarnings++;
}
}
}
var outputValue = Reflect.get(target, prop, receiver);
var wrapped = ModAPI.util.wrap(outputValue, target, corrective, true);
if (wrapped) {
@ -839,7 +862,11 @@ const modapi_postinit = "(" + (() => {
var v = typeof param === "object" ? param.msg : (param + "");
v ||= "";
var jclString = ModAPI.util.string(v);
ModAPI.hooks.methods["nmcg_GuiNewChat_printChatMessage"](ModAPI.javaClient.$ingameGUI.$persistantChatGUI, ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.util.ChatComponentText")].constructors[0](jclString));
if (ModAPI.is_1_12) {
ModAPI.hooks.methods["nmcg_GuiNewChat_printChatMessage"](ModAPI.javaClient.$ingameGUI.$persistantChatGUI, ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.util.text.TextComponentString")].constructors[0](jclString));
} else {
ModAPI.hooks.methods["nmcg_GuiNewChat_printChatMessage"](ModAPI.javaClient.$ingameGUI.$persistantChatGUI, ModAPI.hooks._classMap[ModAPI.util.getCompiledName("net.minecraft.util.ChatComponentText")].constructors[0](jclString));
}
}
ModAPI.util.makeArray = function makeArray(arrayClass, arrayContents = []) {
@ -959,7 +986,7 @@ const modapi_postinit = "(" + (() => {
inlineIntegratedServerStartup = ModAPI.hooks._rippedMethodKeys.filter(key => { return key.startsWith(inlineIntegratedServerStartup); })[0];
const inlineIntegratedServerStartupMethod = ModAPI.hooks.methods[inlineIntegratedServerStartup];
ModAPI.hooks.methods[inlineIntegratedServerStartup] = function (worker, bootstrap) {
var x = inlineIntegratedServerStartupMethod.apply(this, [worker, bootstrap + ";" + globalThis.modapi_postinit + ";" + ModAPI.dedicatedServer._data.join(";")]);
var x = inlineIntegratedServerStartupMethod.apply(this, [worker, bootstrap + ";" + globalThis.modapi_postinit + ";ModAPI.isServer=true;" + ModAPI.dedicatedServer._data.join(";")]);
ModAPI.dedicatedServer._data = [];
ModAPI.dedicatedServer._wasUsed = true;
console.log("[ModAPI] Hooked into inline integrated server.");
@ -1112,6 +1139,11 @@ const modapi_postinit = "(" + (() => {
} else {
ModAPI.enchantments = new Proxy(ModAPI.reflect.getClassById("net.minecraft.enchantment.Enchantment").staticVariables, StaticProps_ProxyConf);
}
if (ModAPI.is_1_12) {
//1.12 specific globals
ModAPI.blockSounds = new Proxy(ModAPI.reflect.getClassById("net.minecraft.block.SoundType").staticVariables, StaticProps_ProxyConf);
}
}
ModAPI.events.newEvent("bootstrap", "server");
@ -1193,11 +1225,16 @@ const modapi_postinit = "(" + (() => {
// 1.12 utility junk
if (ModAPI.is_1_12) {
var Block = ModAPI.reflect.getClassById("net.minecraft.block.Block").class;
var NonNullList = ModAPI.reflect.getClassById("net.minecraft.util.NonNullList").class;
var ArrayAsList = ModAPI.reflect.getClassById("java.util.Arrays$ArrayAsList").class;
easyStaticPropAlias("net.minecraft.block.Block", "REGISTRY", "blockRegistry");
easyStaticPropAlias("net.minecraft.item.Item", "REGISTRY", "itemRegistry");
}
function qhash(txt, arr, interval) {
arr ||= [];
interval ||= 32767;
// var interval = 4095; //used to be 4095 - arr.length, but that increases incompatibility based on load order and other circumstances
if (arr.length >= interval) {
console.error("[ModAPI.keygen] Ran out of IDs while generating for " + txt);
@ -1216,6 +1253,7 @@ const modapi_postinit = "(" + (() => {
}
return hash;
}
ModAPI.util.qhash = qhash;
ModAPI.keygen = {};
@ -1226,11 +1264,19 @@ const modapi_postinit = "(" + (() => {
return registryNamespaceMethod.apply(this, args);
}
ModAPI.keygen.item = function (item) {
var values = [...ModAPI.reflect.getClassById("net.minecraft.item.Item").staticVariables.itemRegistry.$modapi_specmap.values()];
if (ModAPI.is_1_12) {
var values = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.item.Item").staticVariables.REGISTRY).getCorrective().underlyingIntegerMap.identityMap.elementData.filter(x => !!x).map(y => y.value.value);
} else {
var values = [...ModAPI.reflect.getClassById("net.minecraft.item.Item").staticVariables.itemRegistry.$modapi_specmap.values()];
}
return qhash(item, values, 4095);
}
ModAPI.keygen.block = function (block) {
var values = [...ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry.$modapi_specmap.values()];
if (ModAPI.is_1_12) {
var values = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.REGISTRY).getCorrective().underlyingIntegerMap.identityMap.elementData.filter(x => !!x).map(y => y.value.value);
} else {
var values = [...ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry.$modapi_specmap.values()];
}
return qhash(block, values, 4095);
}
ModAPI.keygen.entity = function (entity) {
@ -1248,10 +1294,27 @@ const modapi_postinit = "(" + (() => {
}
return qhash(entity, values, 127);
}
ModAPI.keygen.sound = function (soundId) {
if (!ModAPI.is_1_12) {
return -1;
}
const SoundEvent = ModAPI.reflect.getClassByName("SoundEvent")
const values = ModAPI.util.wrap(SoundEvent.staticVariables.REGISTRY).getCorrective().underlyingIntegerMap.identityMap.elementData.filter(x => !!x).map(y => y.value.value);
return qhash(soundId, values, 4095);
}
ModAPI.keygen.enchantment = function (enchantment) {
const Enchantment = ModAPI.reflect.getClassByName("Enchantment");
if (ModAPI.is_1_12) {
const values = ModAPI.util.wrap(Enchantment.staticVariables.REGISTRY).getCorrective().underlyingIntegerMap.identityMap.elementData.filter(x => !!x).map(y => y.value.value);
return qhash(enchantment, values, 4095);
}
const values = ModAPI.util.wrap(Enchantment.staticVariables.enchantmentsList).getCorrective().filter(x => !!x).map(y => y.effectId);
return qhash(enchantment, values, 4095);
}
}).toString() + ")();";
if (globalThis.process) {
module.exports = {
modapi_postinit: modapi_postinit
}
}
}

View File

@ -34,6 +34,8 @@ The global object has the following properties:
- This is a key-value dictionary of all of the enchantments in the game. It is generated upon init from the static variables of the `Enchantment` class.
- For example, to access the enchantment class for `knockback`, you can use `ModAPI.enchantments["knockback"]`
- As the enchantment class has other static variables, `Object.keys` will also return non-enchantment keys such as `enchantmentsBookList`.
- `ModAPI.blockSounds: Map<String, SoundType>` (1.12 only)
- This is a key-value dictionary of `SoundType`s. Used in `.setSoundType(soundType)` when registering custom blocks in 1.12.
- `ModAPI.minecraft: Minecraft`
- This is the minecraft instance for the client, generated upon init.
- It can also be accessed using `ModAPI.mc`
@ -81,6 +83,8 @@ The global object has the following properties:
- The version of ModAPI.
- `ModAPI.is_1_12: Boolean`
- Property defining wether or not ModAPI thinks the current version is 1.12-based.
- `ModAPI.isServer: Boolean`
- Property defining wether or not ModAPI thinks it is running on the dedicated server.
- `ModAPI.flavour: String`
- The flavour of ModAPI. Hardcoded to be `"injector"`.

View File

@ -7,4 +7,8 @@ Methods:
- `ModAPI.keygen.block(blockId: String) : number`
- Example usage is: `var id = ModAPI.keygen.block("my_example_block");`
- `ModAPI.keygen.entity(entityId: String) : number`
- Example usage is: `var id = ModAPI.keygen.entity("my_example_entity");`
- Example usage is: `var id = ModAPI.keygen.entity("my_example_entity");`
- `ModAPI.keygen.sound(soundId: String) : number`
- Example usage is: `var id = ModAPI.keygen.sound("my.example.sound");`
- `ModAPI.keygen.enchantment(enchantmentId: String) : number`
- Example usage is: `var id = ModAPI.keygen.enchantment("sharpness_v2");`

View File

@ -11,7 +11,7 @@ ModAPI.meta.credits("By ZXMushroom63");
if (ModAPI.is_1_12) {
const _boolRes = ModAPI.reflect.getClassById("net.lax1dude.eaglercraft.internal.teavm.BooleanResult").constructors[0];
booleanResult = (b) => _boolRes(b*1);
booleanResult = (b) => _boolRes(b * 1);
} else {
booleanResult = (b) => ModAPI.hooks.methods.nlevit_BooleanResult__new(b * 1);
}
@ -196,6 +196,26 @@ ModAPI.meta.credits("By ZXMushroom63");
return originalL18NFormat.apply(this, args);
};
const LanguageMapTranslate = ModAPI.util.getMethodFromPackage("net.minecraft.util.text.translation.LanguageMap", "tryTranslateKey");
const originalLanguageMapTranslate = ModAPI.hooks.methods[LanguageMapTranslate];
ModAPI.hooks.methods[LanguageMapTranslate] = function (...args) {
var key = ModAPI.util.ustr(args[1]);
if (AsyncSink.L10N.has(key)) {
args[1] = ModAPI.util.str(AsyncSink.L10N.get(key));
}
return originalLanguageMapTranslate.apply(this, args);
};
const LanguageMapCheckTranslate = ModAPI.util.getMethodFromPackage("net.minecraft.util.text.translation.LanguageMap", "isKeyTranslated");
const originalLanguageMapCheckTranslate = ModAPI.hooks.methods[LanguageMapCheckTranslate];
ModAPI.hooks.methods[LanguageMapCheckTranslate] = function (...args) {
var key = ModAPI.util.ustr(args[1]);
if (AsyncSink.L10N.has(key)) {
return 1;
}
return originalLanguageMapTranslate.apply(this, args);
};
const L10NCheck = ModAPI.util.getMethodFromPackage("net.minecraft.util.StatCollector", "canTranslate");
const originalL10NCheck = ModAPI.hooks.methods[L10NCheck];
ModAPI.hooks.methods[L10NCheck] = function (...args) {
@ -217,7 +237,7 @@ ModAPI.meta.credits("By ZXMushroom63");
async function assureAsyncSinkResources() {
const dec = new TextDecoder("utf-8");
const enc = new TextEncoder("utf-8");
var resourcePackKey = ModAPI.is_1_12 ? "_net_lax1dude_eaglercraft_v1_8_internal_PlatformFilesystem_1_12_2_" :(await indexedDB.databases()).find(x => x?.name?.endsWith("_resourcePacks")).name;
var resourcePackKey = ModAPI.is_1_12 ? "_net_lax1dude_eaglercraft_v1_8_internal_PlatformFilesystem_1_12_2_" : (await indexedDB.databases()).find(x => x?.name?.endsWith("_resourcePacks")).name;
const dbRequest = indexedDB.open(resourcePackKey);
const db = await promisifyIDBRequest(dbRequest);
const transaction = db.transaction(["filesystem"], "readonly");
@ -326,8 +346,8 @@ ModAPI.meta.credits("By ZXMushroom63");
// values = SoundEntry[]
// category: AsyncSink.Audio.Category.*
// SoundEntry = {path: String, pitch: 1, volume: 1, streaming: false}
const EaglercraftRandom = ModAPI.reflect.getClassByName("EaglercraftRandom").constructors.find(x=>x.length===0);
const EaglercraftRandom = ModAPI.reflect.getClassByName("EaglercraftRandom").constructors.find(x => x.length === 0);
function makeSoundEventAccessor(soundpoolentry, weight) {
const SoundEventAccessorClass = ModAPI.reflect.getClassByName("SoundEventAccessor").class;
var object = new SoundEventAccessorClass;
@ -336,7 +356,7 @@ ModAPI.meta.credits("By ZXMushroom63");
wrapped.weight = weight;
return object;
}
function makeSoundEventAccessorComposite(rKey, pitch, volume, category) {
const SoundEventAccessorCompositeClass = ModAPI.reflect.getClassByName("SoundEventAccessorComposite").class;
var object = new SoundEventAccessorCompositeClass;
@ -349,12 +369,12 @@ ModAPI.meta.credits("By ZXMushroom63");
wrapped.rnd = EaglercraftRandom();
return object;
}
function makeSoundEventAccessor112(rKey) {
function makeSoundEventAccessor112(rKey, subttl) {
const SoundEventAccessorClass = ModAPI.reflect.getClassByName("SoundEventAccessor").class;
var object = new SoundEventAccessorClass;
var wrapped = ModAPI.util.wrap(object).getCorrective();
wrapped.location = rKey;
wrapped.subtitle = null;
wrapped.subtitle = ModAPI.util.str(subttl);
wrapped.accessorList = ModAPI.hooks.methods.cgcc_Lists_newArrayList0();
wrapped.rnd = EaglercraftRandom();
return object;
@ -362,32 +382,44 @@ ModAPI.meta.credits("By ZXMushroom63");
if (ModAPI.is_1_12) {
const soundType = ModAPI.reflect.getClassById("net.minecraft.client.audio.Sound$Type").staticVariables.FILE;
const mkSound = ModAPI.reflect.getClassById("net.minecraft.client.audio.Sound").constructors[0];
AsyncSink.Audio.register = function addSfx(key, category, values) {
if (!category) {
throw new Error("[AsyncSink] Invalid audio category provided: "+category);
const SoundEvent = ModAPI.reflect.getClassById("net.minecraft.util.SoundEvent");
const SoundEvents = ModAPI.reflect.getClassById("net.minecraft.init.SoundEvents");
AsyncSink.Audio.register = function addSfx(key, unused, values, subtitle) {
subtitle ||= "(AsyncSink Sound)";
if (unused) {
console.log("Category is not a property of the sound in 1.12, category for " + key + " will be unused.");
}
const rKey = ResourceLocation(ModAPI.util.str(key));
const ev = new SoundEvent.class;
ev.$soundName = rKey;
ModAPI.util.wrap(SoundEvent.staticVariables.REGISTRY).register(ModAPI.keygen.sound(key), rKey, ev);
const outObj = SoundEvents.staticMethods.getRegisteredSoundEvent.method(ModAPI.util.str(key));
var snd = ModAPI.mc.mcSoundHandler.getCorrective();
var registry = snd.soundRegistry.soundRegistry;
var rKey = ResourceLocation(ModAPI.util.str(key));
var soundPool = values.map(se => {
return mkSound(ModAPI.util.str(se.path), se.volume, se.pitch, 1, soundType, 1 * se.streaming);
return mkSound(ModAPI.util.str(se.path.replace("sounds/", "").replace(".ogg", "")), se.volume, se.pitch, 1, soundType, 1 * se.streaming);
});
var eventAccessor = makeSoundEventAccessor112(rKey);
var eventAccessor = makeSoundEventAccessor112(rKey, subtitle);
var eventAccessorWrapped = ModAPI.util.wrap(eventAccessor);
soundPool.forEach(sound => {
eventAccessorWrapped.accessorList.add(sound);
});
AsyncSink.Audio.Objects.push([rKey, eventAccessor]);
registry.put(rKey, eventAccessor);
values.map(x=>"resourcepacks/AsyncSinkLib/assets/minecraft/" + x.path + ".mcmeta").forEach(x=>AsyncSink.setFile(x, new ArrayBuffer(0)));
return soundPool;
values.map(x => "resourcepacks/AsyncSinkLib/assets/minecraft/" + x.path + ".mcmeta").forEach(x => AsyncSink.setFile(x, new ArrayBuffer(0)));
return outObj;
}
} else {
const SoundPoolEntry = ModAPI.reflect.getClassByName("SoundPoolEntry").constructors.find(x => x.length === 4);
AsyncSink.Audio.register = function addSfx(key, category, values) {
if (!category) {
throw new Error("[AsyncSink] Invalid audio category provided: "+category);
throw new Error("[AsyncSink] Invalid audio category provided: " + category);
}
var snd = ModAPI.mc.mcSoundHandler;
var registry = snd.sndRegistry.soundRegistry;
@ -405,7 +437,7 @@ ModAPI.meta.credits("By ZXMushroom63");
});
AsyncSink.Audio.Objects.push([rKey, compositeSound]);
registry.put(rKey, compositeSound);
values.map(x=>"resourcepacks/AsyncSinkLib/assets/minecraft/" + x.path + ".mcmeta").forEach(x=>AsyncSink.setFile(x, new ArrayBuffer(0)));
values.map(x => "resourcepacks/AsyncSinkLib/assets/minecraft/" + x.path + ".mcmeta").forEach(x => AsyncSink.setFile(x, new ArrayBuffer(0)));
return soundPool;
}
}

View File

@ -0,0 +1,122 @@
//THIS IS A DEMO MOD
//nice little utility function to fix the block identity map
function fixupBlockIds() {
var blockRegistry = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry).getCorrective();
var BLOCK_STATE_IDS = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.BLOCK_STATE_IDS).getCorrective();
blockRegistry.registryObjects.hashTableKToV.forEach(entry => {
if (entry) {
var block = entry.value;
var validStates = block.getBlockState().getValidStates();
var stateArray = validStates.array || [validStates.element];
stateArray.forEach(iblockstate => {
var i = blockRegistry.getIDForObject(block.getRef()) << 4 | block.getMetaFromState(iblockstate.getRef());
BLOCK_STATE_IDS.put(iblockstate.getRef(), i);
});
}
});
}
function makeSteveBlock() {
var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block");
var blockSuper = ModAPI.reflect.getSuper(blockClass, (x) => x.length === 2);
var creativeBlockTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabBlock;
function nmb_BlockSteve() {
blockSuper(this, ModAPI.materials.rock.getRef());
//this.$defaultBlockState = this.$blockState.$getBaseState(); built-in in 1.12
this.$setCreativeTab(creativeBlockTab);
}
ModAPI.reflect.prototypeStack(blockClass, nmb_BlockSteve);
globalThis.nmb_BlockSteve = nmb_BlockSteve;
}
function registerSteveClientSide() {
var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item");
var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block");
var block_of_steve = (new nmb_BlockSteve()).$setHardness(-1.0).$setSoundType(ModAPI.blockSounds.PLANT.getRef()).$setUnlocalizedName(
ModAPI.util.str("steve")
);
blockClass.staticMethods.registerBlock0.method(
ModAPI.keygen.block("steve"),
ModAPI.util.str("steve"),
block_of_steve
);
itemClass.staticMethods.registerItemBlock0.method(block_of_steve);
ModAPI.addEventListener("lib:asyncsink", async () => {
ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{
console.log("registered!!");
renderItem.registerBlock(block_of_steve, ModAPI.util.str("steve"));
});
AsyncSink.L10N.set("tile.steve.name", "Block Of Steve");
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/steve.json", JSON.stringify(
{
"parent": "block/cube_all",
"textures": {
"all": "blocks/steve"
}
}
));
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/item/steve.json", JSON.stringify(
{
"parent": "block/steve",
"display": {
"thirdperson": {
"rotation": [10, -45, 170],
"translation": [0, 1.5, -2.75],
"scale": [0.375, 0.375, 0.375]
}
}
}
));
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/blockstates/steve.json", JSON.stringify(
{
"variants": {
"normal": [
{ "model": "steve" },
]
}
}
));
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/blocks/steve.png", await (await fetch(
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE0SURBVDhPpdO9S8NAHMbxy3sVfJmMg6h7FRXXkkUUX0addSjo4OAfIDqLIoiLi3+BRRx0EIQOnV0EcVAIWkR0KIFgrcEktX6vcXD0nuE+5Afhnhw5bWy4qylaidOfVQhT0zFKYozjBHVdzi3TwCZvteaS/0fLD8oGf5OzTeyxNUyE3Ln2HmGctpuxKuS3wd76CgPHsrEj142NeojCkHsFry+4c3aJ6g1OtlZp0Ok4DD4i+Y2GIZ+DMMAhtw+fHu8xi3IDM9t5YfMQF71dLHo+ZjsfXbh4WtnH0vYaqp/BcXGGM3D7BxiYTi+el8uYZWm2gM/VB/Tfaqje4GB5iga2Jv+sUuUa5/ITmOXq7gbnC+MY1r9QvcHG9AgN0lRex1u/ilr7ehqWvBNZvMlRbESfqNhAiG/Pb1bHXpMbFgAAAABJRU5ErkJggg=="
)).arrayBuffer());
});
ModAPI.blocks["steve"] = block_of_steve;
}
function registerSteveServerSide() {
function fixupBlockIds() {
var blockRegistry = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.blockRegistry).getCorrective();
var BLOCK_STATE_IDS = ModAPI.util.wrap(ModAPI.reflect.getClassById("net.minecraft.block.Block").staticVariables.BLOCK_STATE_IDS).getCorrective();
blockRegistry.registryObjects.hashTableKToV.forEach(entry => {
if (entry) {
var block = entry.value;
var validStates = block.getBlockState().getValidStates();
var stateArray = validStates.array || [validStates.element];
stateArray.forEach(iblockstate => {
var i = blockRegistry.getIDForObject(block.getRef()) << 4 | block.getMetaFromState(iblockstate.getRef());
BLOCK_STATE_IDS.put(iblockstate.getRef(), i);
});
}
});
}
var blockClass = ModAPI.reflect.getClassById("net.minecraft.block.Block");
var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item");
ModAPI.addEventListener("bootstrap", () => {
var block_of_steve = (new nmb_BlockSteve()).$setHardness(-1.0).$setSoundType(ModAPI.blockSounds.PLANT.getRef()).$setUnlocalizedName(
ModAPI.util.str("steve")
);
blockClass.staticMethods.registerBlock0.method(
ModAPI.keygen.block("steve"), //MAXIMUM BLOCK ID IS 4095. keygen method accounts for this
ModAPI.util.str("steve"),
block_of_steve
);
itemClass.staticMethods.registerItemBlock0.method(block_of_steve);
fixupBlockIds();
ModAPI.blocks["steve"] = block_of_steve;
});
}
ModAPI.dedicatedServer.appendCode(makeSteveBlock);
makeSteveBlock();
registerSteveClientSide();
fixupBlockIds();
ModAPI.dedicatedServer.appendCode(registerSteveServerSide);

View File

@ -2,20 +2,28 @@
ModAPI.meta.title("Timescale Command");
ModAPI.meta.description("/timescale 0.5 to halve the speed of time");
ModAPI.meta.credits("By ZXMushroom63");
globalThis.timeScale = 1n;
globalThis.timeScaleDividing = false;
PluginAPI.addEventListener("sendchatmessage", (event) => {
if (event.message.toLowerCase().startsWith("/timescale")) {
var speed = parseFloat(event.message.split(" ")[1]);
if (!speed) {
speed = 1;
globalThis.timeScale = 1n;
globalThis.timeScaleDividing = false;
PluginAPI.mc.timer.timerSpeed = 1;
} else {
if (speed < 1) {
speed = 1 / Math.round(1 / speed);
globalThis.timeScaleDividing = true;
globalThis.timeScale = BigInt(Math.round(1 / speed));
} else {
speed = Math.round(speed);
globalThis.timeScaleDividing = false;
globalThis.timeScale = BigInt(Math.round(speed));
}
PluginAPI.mc.timer.timerSpeed = speed;
}
PluginAPI.mc.systemTime = PluginAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.Minecraft", "getSystemTime")]();
PluginAPI.mc.debugUpdateTime = PluginAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.Minecraft", "getSystemTime")]() - 1000n;
PluginAPI.mc.timer.lastSyncSysClock = PluginAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.Minecraft", "getSystemTime")]();
PluginAPI.displayToChat("[Timescale] Set world timescale to " + speed.toFixed(2) + ".");
}
});
@ -52,4 +60,12 @@
}
};
});
const original_getSystemTime = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.Minecraft", "getSystemTime")];
PluginAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.client.Minecraft", "getSystemTime")] = function () {
if (globalThis.timeScaleDividing) {
return original_getSystemTime() / globalThis.timeScale;
} else {
return original_getSystemTime() * globalThis.timeScale;
}
};
})();

View File

@ -0,0 +1,69 @@
// This is an example mod on how to register an item.
(()=>{
const itemTexture = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAKZJREFUOE9j/P//PxMDBIBoEP6HREOl4PLIciA2AyPIgMcM//7KgvWSDJjBBpx9/+YvJzc3Sbq12DhB6sEGsJ19/+YnmQawYhigzc7FcPXnN4KugbqAHWQAy9n3b34T4wJkw6EGYLqAoNVQBWS5ANlwZBfAvUCs/0EGkW0AzBKqGoCSDgh5A80F2KMRpAgfAKUT6kcjsfEPUycmKMQgy8AETkgUZWcAS3CPIf4oSPsAAAAASUVORK5CYII=";
//this texture is REALLY bad, so the item appears 2d in game. (it uses partially transparent pixels around the edges in some spots ;-;)
ModAPI.meta.title("Adding items demo.");
ModAPI.meta.version("v1.0");
ModAPI.meta.icon(itemTexture);
ModAPI.meta.description("Requires AsyncSink.");
function ExampleItem() {
var creativeMiscTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.MISC;
var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item");
var itemSuper = ModAPI.reflect.getSuper(itemClass, (x) => x.length === 1);
function nmi_ItemExample() {
itemSuper(this); //Use super function to get block properties on this class.
this.$setCreativeTab(creativeMiscTab);
}
ModAPI.reflect.prototypeStack(itemClass, nmi_ItemExample);
function internal_reg() {
var example_item = (new nmi_ItemExample()).$setUnlocalizedName(
ModAPI.util.str("exampleitem")
);
itemClass.staticMethods.registerItem.method(ModAPI.keygen.item("exampleitem"), ModAPI.util.str("exampleitem"), example_item);
ModAPI.items["exampleitem"] = example_item;
return example_item;
}
if (ModAPI.items) {
return internal_reg();
} else {
ModAPI.addEventListener("bootstrap", internal_reg);
}
}
ModAPI.dedicatedServer.appendCode(ExampleItem);
var example_item = ExampleItem();
ModAPI.addEventListener("lib:asyncsink", async () => {
ModAPI.addEventListener("lib:asyncsink:registeritems", (renderItem)=>{
renderItem.registerItem(example_item, ModAPI.util.str("exampleitem"));
});
AsyncSink.L10N.set("item.exampleitem.name", "Example Item");
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/item/exampleitem.json", JSON.stringify(
{
"parent": "builtin/generated",
"textures": {
"layer0": "items/exampleitem"
},
"display": {
"thirdperson": {
"rotation": [ -90, 0, 0 ],
"translation": [ 0, 1, -3 ],
"scale": [ 0.55, 0.55, 0.55 ]
},
"firstperson": {
"rotation": [ 0, -135, 25 ],
"translation": [ 0, 4, 2 ],
"scale": [ 1.7, 1.7, 1.7 ]
}
}
}
));
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/items/exampleitem.png", await (await fetch(
itemTexture
)).arrayBuffer());
});
})();

View File

@ -1,6 +1,6 @@
{
"name": "eaglerforgeinjector",
"version": "2.7.92",
"version": "2.7.93",
"description": "Advanced modding API injector for unminified, unobfuscated, unsigned eaglercraft builds.",
"main": "node.js",
"directories": {