Merge pull request #46 from eaglerforge/main

Push latest batch of changes to stable
This commit is contained in:
ZXMushroom63 2024-12-11 22:26:54 +08:00 committed by GitHub
commit 8e6f2b1774
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 537 additions and 19 deletions

View File

@ -11,4 +11,7 @@ Current features:
Go to https://eaglerforge.github.io/EaglerForgeInjector/ and upload an unminified, unobfuscated, unsigned EaglercraftX offline download.
#### Portable Offline
Download this repository as a .zip, and extract it. Open index.html with your preferred browser (use `ctrl` + `O` on a new tab) and upload an unminified, unobfuscated, unsigned EaglercraftX offline download.
Download this repository as a .zip, and extract it. Open index.html with your preferred browser (use `ctrl` + `O` on a new tab) and upload an unminified, unobfuscated, unsigned EaglercraftX offline download.
## Discord server
[https://discord.gg/rbxN7kby5W](https://discord.gg/rbxN7kby5W)

View File

@ -1 +1 @@
# Coming Soon
## Coming Soon / It's not done yet.

View File

@ -1,6 +1,6 @@
## Custom Blocks Tutorial With ModAPI
## Custom Block Tutorial With ModAPI
This tutorial will show you how to make custom blocks with ModAPI. It will use my AsyncSink library to load the resources for the block.
This tutorial will be making a block with the durability of dirt that explodes when broken.
We'll be making a block with the durability of dirt that explodes when broken.
As always, we'll start with the default boilerplate starter code:
```javascript
@ -8,7 +8,7 @@ As always, we'll start with the default boilerplate starter code:
ModAPI.meta.title("Custom Block Demo");
ModAPI.meta.version("v1.0");
ModAPI.meta.description("Adds a block that blows up when used.");
ModAPI.meta.credits("By ZXMushroom63");
ModAPI.meta.credits("By <author_name>");
})();
```
Let's get our blocks texture done ahead of time.
@ -20,7 +20,7 @@ Store this at the beginning of the function using a constant. Also use that cons
ModAPI.meta.title("Custom Block Demo");
ModAPI.meta.version("v1.0");
ModAPI.meta.description("Adds a block that blows up when used.");
ModAPI.meta.credits("By ZXMushroom63");
ModAPI.meta.credits("By <author_name>");
ModAPI.meta.icon(texture);
})();
@ -188,7 +188,7 @@ When it's loaded, we'll:
});
AsyncSink.L10N.set("tile.custom_block.name", "My Custom Block"); //Set the name of the block
//Required boilerplate for block and item models.
//Make an in-memory resource pack for the block. This is standard between, EaglerForge, Forge, Fabric, and NeoForge (pretty much any modding API)
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/block/custom_block.json", JSON.stringify(
{
"parent": "block/cube_all",

View File

@ -0,0 +1,127 @@
## Custom Item Tutorial with ModAPI
This tutorial will cover making custom items with ModAPI. It is recommended that you follow the [custom block tutorial](custom_block.md) first, as this tutorial is more fast-paced. The custom item we'll be adding will set the player's velocity to a random value when used, to demonstrate how methods can be overridden.
We'll begin with a constant item texture encoded into a data URI, as well as some metadata.
```javascript
(function CustomItemMod() {
const itemTexture = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAKZJREFUOE9j/P//PxMDBIBoEP6HREOl4PLIciA2AyPIgMcM//7KgvWSDJjBBpx9/+YvJzc3Sbq12DhB6sEGsJ19/+YnmQawYhigzc7FcPXnN4KugbqAHWQAy9n3b34T4wJkw6EGYLqAoNVQBWS5ANlwZBfAvUCs/0EGkW0AzBKqGoCSDgh5A80F2KMRpAgfAKUT6kcjsfEPUycmKMQgy8AETkgUZWcAS3CPIf4oSPsAAAAASUVORK5CYII=";
ModAPI.meta.title("Custom Item Mod");
ModAPI.meta.icon(itemTexture);
ModAPI.meta.description("it's a custom item. what more do you want");
ModAPI.meta.credits("By <author_name>");
})();
```
Add a function that will contain the code to register the item called `CustomItem`. Inside it we'll do something really similar to the custom blocks tutorial, where we'll define a custom item class, and then register it (or, if on the server, wait until the `bootstrap` event fires). The reason it's contained in a function is to make the job of running the same code on the server and client easier.
```javascript
(function CustomItemMod() {
//...
function CustomItem() {
var creativeMiscTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabMisc; //chuck it in the miscellaneous category ig
var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item"); //Get the item class
var itemSuper = ModAPI.reflect.getSuper(itemClass, (x) => x.length === 1); //Get the super() function of the item class that has a length of 1
function CustomItem() {
itemSuper(this); //Use super function to get block properties on this class.
this.$setCreativeTab(creativeMiscTab); //Set the creative tab of the item to be the misc tab
}
ModAPI.reflect.prototypeStack(itemClass, CustomItem); // ModAPI equivalent of `extends` in java
CustomItem.prototype.$onItemRightClick = function ($itemstack, $world, $player) { //example of how to override a method
//use ModAPI.util.wrap to create a proxy of the player and the world without $ prefixes on the properties and methods
var player = ModAPI.util.wrap($player);
var world = ModAPI.util.wrap($world);
if (!world.isRemote) { //If we are on the server
// Math.random() returns a number from 0.0 to 1.0, so we subtract 0.5 and then multiply by 2 to make it become -1.0 to 1.0 instead
player.motionX += (Math.random() - 0.5) * 3;
player.motionZ += (Math.random() - 0.5) * 3;
player.motionY += Math.random() * 1.5; // gravity is a thing, so no negative numbers here otherwise it'll be boring
}
return $itemstack;
}
// Internal registration function. This will be used to actually register the item on both the client and the server.
function internal_reg() {
// Construct an instance of the CustomItem, and set it's unlocalized name (translation id)
var custom_item = (new CustomItem()).$setUnlocalizedName(
ModAPI.util.str("custom_item")
);
//Register it using ModAPI.keygen() to get the item id.
itemClass.staticMethods.registerItem.method(ModAPI.keygen.item("custom_item"), ModAPI.util.str("custom_item"), custom_item);
//Expose it to ModAPI
ModAPI.items["custom_item"] = custom_item;
//return the instance.
return custom_item;
}
//if the item global exists (and it will on the client), register the item and return the registered instance.
if (ModAPI.items) {
return internal_reg();
} else {
//Otherwise attatch the registration method to the bootstrap method.
ModAPI.addEventListener("bootstrap", internal_reg);
}
}
})();
```
Now let's run the `CustomItem` function on the server and the client, and then use [AsyncSink](../../examplemods/AsyncSink.js) to create an in-memory resource pack to load item textures and models!
```javascript
(function CustomItemMod() {
const itemTexture = "...";
ModAPI.meta.title("Custom Item Mod");
ModAPI.meta.icon(itemTexture);
ModAPI.meta.description("it's a custom item. what more do you want");
ModAPI.meta.credits("By <author_name>");
function CustomItem() {
// ...
}
// Run the function when the dedicated server loads.
ModAPI.dedicatedServer.appendCode(CustomItem);
// Run the function on the client
var custom_item = CustomItem();
ModAPI.addEventListener("lib:asyncsink", async () => {
ModAPI.addEventListener("custom:asyncsink_reloaded", ()=>{
ModAPI.mc.renderItem.registerItem(custom_item, ModAPI.util.str("custom_item"));
});
AsyncSink.L10N.set("item.custom_item.name", "Cool Custom Item");
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/item/custom_item.json", JSON.stringify(
{
"parent": "builtin/generated",
"textures": {
"layer0": "items/custom_item"
},
"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/custom_item.png", await (await fetch(
itemTexture
)).arrayBuffer());
});
})();
```
That's it! Upload your completed mod, run `.reload_tex` in chat in a singleplayer world and use your new item!\
[completed mod]()

View File

@ -34,5 +34,5 @@ Prerequisites:
Tutorials:
- [Custom Blocks](custom_block.md)
- [Custom Items](comingsoon)
- [Timescale Command](comingsoon)
- [Custom Items](custom_item.md)
- [Timescale Command](timescale.md)

103
docs/tutorials/timescale.md Normal file
View File

@ -0,0 +1,103 @@
## Timescale Mod with ModAPI
This mod will cover adding a new command that controls the speed that Eaglercraft runs at. This tutorial assumes that you have some knowledge on how to use [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) in JavaScript.
Let's get our basic template code down:
```javascript
(function TimescaleCommand() {
ModAPI.meta.title("Timescale Command");
ModAPI.meta.description("use /timescale to control time");
ModAPI.meta.credits("By <author_name>");
})()
```
Our mod is going to be split into 2 distinct parts: client-side and server-side. The client will modify `ModAPI.mc.timer.timerSpeed` when a `/timescale` command is sent, while the server will set the timescale to a global variable, and then modify `net.minecraft.server.MinecraftServer`'s `getCurrentTimeMillis()` to change the rate calculations happen at on the server.\
\
However, there is a slight logistical problem: the server gets the time in milliseconds as a JavaScript [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt), which means we can't just multiply it by a number, we have to either multiply or divide it by another BigInt.
```javascript
var x = 1n * 1.0; //TypeError
var x = 1n * 1n; //Success
```
To allow us to both speed up and slow down time, we'll need to check if the speed inputted is greater or equal to 1. If it is, we round it and then convert it to a `BigInt`, which we'll store on the `globalThis` object. If it's less we'll need to find the speed to the power of -1, or `1 / speed`. We can then round this value and convert that to a `BigInt` to store on `globalThis`. We'll also set `globalThis.timeScaleDividing` to `true`, to signal to replaced `getCurrentTimeMillis()` to divide by the `BigInt` speed factor instead of multiply.\
\
Finally, due to our `BigInt` rounding shenanigans on the server, we have to replicate the rounding inaccuracy on the client.\
\
Let's implement the client side part.
```javascript
(function TimescaleCommand() {
ModAPI.meta.title("Timescale Command");
ModAPI.meta.description("use /timescale to control time");
ModAPI.meta.credits("By <author_name>");
ModAPI.addEventListener("sendchatmessage", (event) => { // before a message gets sent to the server
if (event.message.toLowerCase().startsWith("/timescale")) { //if it is the timescale command
var speed = parseFloat(event.message.split(" ")[1]); //get the part of the message after the space
if (!speed) { //If it doesn't exist, set it to 1.
speed = 1;
} else { //If it does exist:
if (speed < 1) { //When the speed is less than 1, round the denominator (1 over x)
speed = 1 / Math.round(1 / speed);
} else {
// When the speed is greater or equal to 1, round the numerator (x over 1)
speed = Math.round(speed);
}
// Set the speed
ModAPI.mc.timer.timerSpeed = speed;
}
// Log the speed to chat
ModAPI.displayToChat("[Timescale] Set world timescale to " + speed.toFixed(2) + ".");
}
});
})()
```
Now for the serverside part.
```javascript
(function TimescaleCommand() {
//...
ModAPI.dedicatedServer.appendCode(function () { // Run on the server
globalThis.timeScale = 1n; // Initialize globalThis.timeScale
globalThis.timeScaleDividing = false; // Initialize globalThis.timeScaleDividing
ModAPI.addEventListener("processcommand", (event) => { // when the server receives a command
if (event.command.toLowerCase().startsWith("/timescale")) { // if it is a timescale command
var speed = parseFloat(event.command.split(" ")[1]); // get the second part of the command (the speed of time)
if (!speed) { // If it doesn't exist, set it to 1.
globalThis.timeScale = 1n;
globalThis.timeScaleDividing = false;
} else { // If it does exist:
if (speed < 1) {
// When the speed is less than 1, round the denominator (1 over x)
// And enable division mode
globalThis.timeScaleDividing = true;
globalThis.timeScale = BigInt(Math.round(1 / speed));
} else {
// When the speed is greater or equal to 1, round the numerator (x over 1)
// And disable division mode
globalThis.timeScaleDividing = false;
globalThis.timeScale = BigInt(Math.round(speed));
}
}
if (ModAPI.server) { //If the server is initialized
//Bump the current time forward so the server doesn't try to play catch-up
ModAPI.server.currentTime = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.server.MinecraftServer", "getCurrentTimeMillis")]();
}
//Prevent the command not found error from appearing
event.preventDefault = true;
}
});
//Monkey patch the getCurrentTime function.
const original_getCurrentTime = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.server.MinecraftServer", "getCurrentTimeMillis")];
ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.server.MinecraftServer", "getCurrentTimeMillis")] = function () {
if (globalThis.timeScaleDividing) { //If we are in divide mode
return original_getCurrentTime() / globalThis.timeScale; //Return the current time divided by the time scale
} else {
return original_getCurrentTime() * globalThis.timeScale; //Else, return the current time multiplied by the time scale
}
};
});
})()
```

View File

@ -3,7 +3,7 @@
ModAPI.meta.title("Custom Block Demo");
ModAPI.meta.version("v1.0");
ModAPI.meta.description("Adds a block that blows up when used.");
ModAPI.meta.credits("By ZXMushroom63");
ModAPI.meta.credits("By <author_name>");
ModAPI.meta.icon(texture);

View File

@ -0,0 +1,94 @@
(function CustomItemMod() {
const itemTexture = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAKZJREFUOE9j/P//PxMDBIBoEP6HREOl4PLIciA2AyPIgMcM//7KgvWSDJjBBpx9/+YvJzc3Sbq12DhB6sEGsJ19/+YnmQawYhigzc7FcPXnN4KugbqAHWQAy9n3b34T4wJkw6EGYLqAoNVQBWS5ANlwZBfAvUCs/0EGkW0AzBKqGoCSDgh5A80F2KMRpAgfAKUT6kcjsfEPUycmKMQgy8AETkgUZWcAS3CPIf4oSPsAAAAASUVORK5CYII=";
ModAPI.meta.title("Custom Item Mod");
ModAPI.meta.icon(itemTexture);
ModAPI.meta.description("it's a custom item. what more do you want");
ModAPI.meta.credits("By <author_name>");
function CustomItem() {
var creativeMiscTab = ModAPI.reflect.getClassById("net.minecraft.creativetab.CreativeTabs").staticVariables.tabMisc; //chuck it in the miscellaneous category ig
var itemClass = ModAPI.reflect.getClassById("net.minecraft.item.Item"); //Get the item class
var itemSuper = ModAPI.reflect.getSuper(itemClass, (x) => x.length === 1); //Get the super() function of the item class that has a length of 1
function CustomItem() {
itemSuper(this); //Use super function to get block properties on this class.
this.$setCreativeTab(creativeMiscTab); //Set the creative tab of the item to be the misc tab
}
ModAPI.reflect.prototypeStack(itemClass, CustomItem); // ModAPI equivalent of `extends` in java
CustomItem.prototype.$onItemRightClick = function ($itemstack, $world, $player) { //example of how to override a method
//use ModAPI.util.wrap to create a proxy of the player and the world without $ prefixes on the properties and methods
var player = ModAPI.util.wrap($player);
var world = ModAPI.util.wrap($world);
if (!world.isRemote) { //If we are on the server
// Math.random() returns a number from 0.0 to 1.0, so we subtract 0.5 and then multiply by 2 to make it become -1.0 to 1.0 instead
player.motionX += (Math.random() - 0.5) * 3;
player.motionZ += (Math.random() - 0.5) * 3;
player.motionY += Math.random() * 1.5; // gravity is a thing, so no negative numbers here otherwise it'll be boring
}
return $itemstack;
}
// Internal registration function. This will be used to actually register the item on both the client and the server.
function internal_reg() {
// Construct an instance of the CustomItem, and set it's unlocalized name (translation id)
var custom_item = (new CustomItem()).$setUnlocalizedName(
ModAPI.util.str("custom_item")
);
//Register it using ModAPI.keygen() to get the item id.
itemClass.staticMethods.registerItem.method(ModAPI.keygen.item("custom_item"), ModAPI.util.str("custom_item"), custom_item);
//Expose it to ModAPI
ModAPI.items["custom_item"] = custom_item;
//return the instance.
return custom_item;
}
//if the item global exists (and it will on the client), register the item and return the registered instance.
if (ModAPI.items) {
return internal_reg();
} else {
//Otherwise attatch the registration method to the bootstrap method.
ModAPI.addEventListener("bootstrap", internal_reg);
}
}
// Run the function when the dedicated server loads.
ModAPI.dedicatedServer.appendCode(CustomItem);
// Run the function on the client
var custom_item = CustomItem();
ModAPI.addEventListener("lib:asyncsink", async () => {
ModAPI.addEventListener("custom:asyncsink_reloaded", ()=>{
ModAPI.mc.renderItem.registerItem(custom_item, ModAPI.util.str("custom_item"));
});
AsyncSink.L10N.set("item.custom_item.name", "Cool Custom Item");
AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/models/item/custom_item.json", JSON.stringify(
{
"parent": "builtin/generated",
"textures": {
"layer0": "items/custom_item"
},
"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/custom_item.png", await (await fetch(
itemTexture
)).arrayBuffer());
});
})();

View File

@ -34,7 +34,7 @@
var recipeInternal = [];
Object.keys(recipeLegend).forEach((key) => {
recipeInternal.push(ToChar(key));
var ingredient = ModAPI.blocks[recipeLegend[key].id].getRef();
var ingredient = (recipeLegend[key].type === "block" ? ModAPI.blocks : ModAPI.items)[recipeLegend[key].id].getRef();
recipeInternal.push(ingredient);
});

View File

@ -1,6 +1,8 @@
(() => {
ModAPI.meta.title("Fullscreen Fixer");
ModAPI.meta.description("Makes HTML guis still appear in fullscreen.");
ModAPI.meta.credits("By ZXMushroom63");
var oldF11 = HTMLElement.prototype.requestFullscreen;
HTMLElement.prototype.requestFullscreen = function () {
if (this instanceof HTMLBodyElement) {

View File

@ -0,0 +1,185 @@
(()=>{
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 recoilSpeed = 0; //recoil controller
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);
}
ModAPI.addEventListener("update", ()=>{ //recoil update loop (client)
ModAPI.player.rotationPitch -= recoilSpeed;
recoilSpeed *= 0.7;
});
function entityRayCast(player, world, range) {
const HEADSHOT_MAX_DISTANCE_FROM_HEAD = 0.72;
var eyePosition = player.getPositionEyes(0.0);
var targetPosition = player.rayTrace(range, 0).hitVec;
var entities = world.getEntitiesWithinAABBExcludingEntity(
player.getRef(),
player.getEntityBoundingBox().expand(range, range, range).getRef()
).getCorrective().array;
var closestEntity = null;
var isHeadshot = false;
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;
isHeadshot = entity.getPositionEyes(0.0).distanceTo(intercept.hitVec.getRef()) < HEADSHOT_MAX_DISTANCE_FROM_HEAD;
}
}
}
var rayTraceResult = closestEntity;
if (rayTraceResult != null){
return {entity: rayTraceResult, headshot: isHeadshot};
} 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 = DamageSourceClass.staticVariables.cactus;
var world = ModAPI.util.wrap($world);
var entityplayer = ModAPI.util.wrap($player);
var shotentitydata = entityRayCast(entityplayer, world, 16.0);
if (shotentitydata != null){
if (world.isRemote) {
recoilSpeed += 4;
} else {
shotentitydata.entity.attackEntityFrom(cactus, 10 + (16 * shotentitydata.headshot));
if (shotentitydata.headshot) {
console.log("H E A D S H O T");
}
world.playSoundAtEntity(entityplayer.getRef(), ModAPI.util.str("tile.piston.out"), 1.0, 1.8);
}
} else if (!world.isRemote) {
world.playSoundAtEntity(entityplayer.getRef(), ModAPI.util.str("random.click"), 1.0, 1.8);
}
return $itemstack;
}
async function addGunRecipe(gunItem) {
var ObjectClass = ModAPI.reflect.getClassById("java.lang.Object").class;
function ToChar(char) {
return ModAPI.reflect.getClassById("java.lang.Character").staticMethods.valueOf.method(char[0].charCodeAt(0));
}
// Define the recipe legend to map characters to items
var recipeLegend = {
"I": {
type: "item",
id: "iron_ingot" // Using dirt blocks
},
"C": {
type: "block",
id: "iron_block" // Using dirt blocks
},
"Q": {
type: "item",
id: "gunpowder" // Using dirt blocks
},
};
// Define the crafting grid pattern for the recipe
var recipePattern = [
"IIC",
" QI"
];
// Convert the recipe pattern and legend into the required format
var recipeInternal = [];
Object.keys(recipeLegend).forEach((key) => {
recipeInternal.push(ToChar(key));
var ingredient = (recipeLegend[key].type === "block" ? ModAPI.blocks : ModAPI.items)[recipeLegend[key].id].getRef();
recipeInternal.push(ingredient);
});
var recipeContents = recipePattern.flatMap(row => ModAPI.util.str(row));
var recipe = ModAPI.util.makeArray(ObjectClass, recipeContents.concat(recipeInternal));
var resultItem = ModAPI.reflect.getClassById("net.minecraft.item.ItemStack").constructors[4](gunItem, 1);
// Register the recipe with CraftingManager
var craftingManager = ModAPI.reflect.getClassById("net.minecraft.item.crafting.CraftingManager").staticMethods.getInstance.method();
ModAPI.hooks.methods.nmic_CraftingManager_addRecipe(craftingManager, resultItem, recipe);
}
function internal_reg() {
var pistol_item = (new nmi_ItemPistol()).$setUnlocalizedName(
ModAPI.util.str("pistol")
).$setMaxStackSize(1);
itemClass.staticMethods.registerItem.method(ModAPI.keygen.item("pistol"), ModAPI.util.str("pistol"), pistol_item);
ModAPI.items["pistol"] = pistol_item;
addGunRecipe(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());
});
})();

View File

@ -1,16 +1,20 @@
(() => {
ModAPI.meta.title("Timescale Command");
ModAPI.meta.description("/timescale 0.5 to halve the speed of time");
ModAPI.meta.credits("By ZXMushroom63");
PluginAPI.addEventListener("sendchatmessage", (event) => {
if (event.message.toLowerCase().startsWith("/timescale")) {
var speed = parseFloat(event.message.split(" ")[1]);
if (!speed) {
PluginAPI.javaClient.$timer.$timerSpeed = 1;
speed = 1;
PluginAPI.mc.timer.timerSpeed = 1;
} else {
if (speed < 1) {
speed = 1 / Math.round(1 / speed);
} else {
speed = Math.round(speed);
}
PluginAPI.javaClient.$timer.$timerSpeed = speed;
PluginAPI.mc.timer.timerSpeed = speed;
}
PluginAPI.displayToChat("[Timescale] Set world timescale to " + speed.toFixed(2) + ".");
}
@ -34,13 +38,13 @@
}
}
if (ModAPI.server) {
ModAPI.server.currentTime = PluginAPI.hooks.methods.nms_MinecraftServer_getCurrentTimeMillis();
ModAPI.server.currentTime = PluginAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.server.MinecraftServer", "getCurrentTimeMillis")]();
}
event.preventDefault = true;
}
});
const original_getCurrentTime = ModAPI.hooks.methods.nms_MinecraftServer_getCurrentTimeMillis;
PluginAPI.hooks.methods.nms_MinecraftServer_getCurrentTimeMillis = function () {
const original_getCurrentTime = ModAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.server.MinecraftServer", "getCurrentTimeMillis")];
PluginAPI.hooks.methods[ModAPI.util.getMethodFromPackage("net.minecraft.server.MinecraftServer", "getCurrentTimeMillis")] = function () {
if (globalThis.timeScaleDividing) {
return original_getCurrentTime() / globalThis.timeScale;
} else {

View File

@ -38,7 +38,7 @@
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, 0);
blockpos.getZ() + 0.5, 9, 1, 1);
}
return breakBlockMethod(this, $world, $blockpos, $blockstate);
}

View File

@ -1,7 +1,7 @@
// 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 baad, so the item appears 2d in game.
//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);
@ -11,7 +11,7 @@
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_ItemExample = function nmi_ItemExample() {
function nmi_ItemExample() {
itemSuper(this); //Use super function to get block properties on this class.
this.$setCreativeTab(creativeMiscTab);
}