From d581ea7cee4d5b7df9606f671656bb0be0fdbf91 Mon Sep 17 00:00:00 2001 From: Shaun Chong <59573921+data-miner00@users.noreply.github.com> Date: Sat, 8 Jun 2024 06:43:11 +0800 Subject: [PATCH 01/33] Fix typos (#3381) * fix: Address typos * docs: Fix document typos * docs: Fix another typos --------- Co-authored-by: data-miner00 --- docs/api.md | 24 ++++++++++++------------ docs/tutorial.md | 2 +- examples/advanced/chest_confirm.md | 2 +- examples/chatterbox.js | 2 +- examples/looker.js | 2 +- examples/perfectShotBow.js | 2 +- examples/python/chatterbox.py | 2 +- lib/plugins/chat.js | 2 +- test/externalTests/placeEntity.js | 8 ++++---- test/externalTests/plugins/testCommon.js | 2 +- 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/api.md b/docs/api.md index 0e09d2da7..fe8bfc54f 100644 --- a/docs/api.md +++ b/docs/api.md @@ -974,7 +974,7 @@ Default true, whether or not you receive color codes in chats from the server. #### bot.settings.viewDistance -Can be a string listed below or a postive number. +Can be a string listed below or a positive number. Choices: * `far` (default) * `normal` @@ -1035,7 +1035,7 @@ saturation of 5.0. Eating food increases the saturation as well as the food bar. #### bot.oxygenLevel -Number in the range [0, 20] respresenting the number of water-icons known as oxygen level. +Number in the range [0, 20] representing the number of water-icons known as oxygen level. #### bot.physics @@ -1554,7 +1554,7 @@ Fires when a particle is created #### bot.blockAt(point, extraInfos=true) -Returns the block at `point` or `null` if that point is not loaded. If `extraInfos` set to true, also returns informations about signs, paintings and block entities (slower). +Returns the block at `point` or `null` if that point is not loaded. If `extraInfos` set to true, also returns information about signs, paintings and block entities (slower). See `Block`. #### bot.waitForChunksToLoad() @@ -1596,8 +1596,8 @@ Finds the closest blocks from the given point. - `point` - The start position of the search (center). Default is the bot position. - `matching` - A function that returns true if the given block is a match. Also supports this value being a block id or array of block ids. - `useExtraInfo` - To preserve backward compatibility can result in two behavior depending on the type - - **boolean** - Provide your `matching` function more data - noticeably slower aproach - - **function** - Creates two stage maching, if block passes `matching` function it is passed further to `useExtraInfo` with additional info + - **boolean** - Provide your `matching` function more data - noticeably slower approach + - **function** - Creates two stage matching, if block passes `matching` function it is passed further to `useExtraInfo` with additional info - `maxDistance` - The furthest distance for the search, defaults to 16. - `count` - Number of blocks to find before returning the search. Default to 1. Can return less if not enough blocks are found exploring the whole area. @@ -1627,7 +1627,7 @@ with `metadata`. #### bot.recipesAll(itemType, metadata, craftingTable) -The same as bot.recipesFor except that it does not check wether the bot has enough materials for the recipe. +The same as bot.recipesFor except that it does not check whether the bot has enough materials for the recipe. #### bot.nearestEntity(match = (entity) => { return true }) @@ -1635,7 +1635,7 @@ Return the nearest entity to the bot, matching the function (default to all enti Example: ```js -const cow = bot.nearestEntity(entity => entity.name.toLowerCase() === 'cow') // we use .toLowercase() because in 1.8 cow was capitalized, for newer versions that can be ommitted +const cow = bot.nearestEntity(entity => entity.name.toLowerCase() === 'cow') // we use .toLowercase() because in 1.8 cow was capitalized, for newer versions that can be omitted ``` ### Methods @@ -1657,7 +1657,7 @@ Requests chat completion from the server. * `str` - String to complete. * `assumeCommand` - Field sent to server, defaults to false. * `sendBlockInSight` - Field sent to server, defaults to true. Set this option to false if you want more performance. - * `timeout` - Timeout in milliseconds, after which the function will return an ampty array, defaults to 5000. + * `timeout` - Timeout in milliseconds, after which the function will return an empty array, defaults to 5000. #### bot.chat(message) @@ -1683,11 +1683,11 @@ Adds a regex pattern to the bot's chat matching. Useful for bukkit servers where make an event that is called every time the pattern is matched to a message, the event will be called `"chat:name"`, with name being the name passed * `name` - the name used to listen for the event -* `pattern` - regular expression to match to messages recieved +* `pattern` - regular expression to match to messages received * `chatPatternOptions` - object * `repeat` - defaults to true, whether to listen for this event after the first match * `parse` - instead of returning the actual message that was matched, return the capture groups from the regex - * `deprecated` - (**unstable**) used by bot.chatAddPattern to keep compatability, likely to be removed + * `deprecated` - (**unstable**) used by bot.chatAddPattern to keep compatibility, likely to be removed returns a number which can be used with bot.removeChatPattern() to only delete this pattern @@ -1695,10 +1695,10 @@ returns a number which can be used with bot.removeChatPattern() to only delete t #### bot.addChatPatternSet(name, patterns, chatPatternOptions) -make an event that is called every time all patterns havee been matched to messages, +make an event that is called every time all patterns have been matched to messages, the event will be called `"chat:name"`, with name being the name passed * `name` - the name used to listen for the event -* `patterns` - array of regular expression to match to messages recieved +* `patterns` - array of regular expression to match to messages received * `chatPatternOptions` - object * `repeat` - defaults to true, whether to listen for this event after the first match * `parse` - instead of returning the actual message that was matched, return the capture groups from the regex diff --git a/docs/tutorial.md b/docs/tutorial.md index 849941831..06e44b962 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -453,7 +453,7 @@ The function could also be called when an error occurs. Below is an example of a bot that will craft oak logs into oak planks and then into sticks. -Incorect approach ❌: +Incorrect approach ❌: ```js function craft (bot) { diff --git a/examples/advanced/chest_confirm.md b/examples/advanced/chest_confirm.md index 5e2792337..814ff2e0a 100644 --- a/examples/advanced/chest_confirm.md +++ b/examples/advanced/chest_confirm.md @@ -1,4 +1,4 @@ -# Menual Chest Confirm +# Manual Chest Confirm This code snippet will tell the bot not to wait for chest confirmations that some spigot plugins will not send diff --git a/examples/chatterbox.js b/examples/chatterbox.js index 0305a4e4f..0e6d015ab 100644 --- a/examples/chatterbox.js +++ b/examples/chatterbox.js @@ -10,7 +10,7 @@ * may flood the chat, feel free to check them out for other purposes though. * * This bot also replies to some specific chat messages so you can ask him - * a few informations while you are in game. + * a few information while you are in game. */ const mineflayer = require('mineflayer') const { Vec3 } = require('vec3') diff --git a/examples/looker.js b/examples/looker.js index 34a4004ac..d0bb8f9ba 100644 --- a/examples/looker.js +++ b/examples/looker.js @@ -1,5 +1,5 @@ /* - * This script will automaticly look at the closest entity. + * This script will automatically look at the closest entity. * It checks for a near entity every tick. */ const mineflayer = require('mineflayer') diff --git a/examples/perfectShotBow.js b/examples/perfectShotBow.js index db6aec7c2..a6fa12186 100644 --- a/examples/perfectShotBow.js +++ b/examples/perfectShotBow.js @@ -30,7 +30,7 @@ bot.on('spawn', function () { // Auto attack every 1,2 secs with bow // With crossbow attack when crossbow is charget (enchant 3 = 0.5s) - // ['snowball', 'ender_pearl', 'egg', 'splash_potion'] auto attack every 0,1 sec, no recomended use autoAttack for these items, instead use "bot.hawkEye.oneShot(target, weapon)" + // ['snowball', 'ender_pearl', 'egg', 'splash_potion'] auto attack every 0,1 sec, no recommended use autoAttack for these items, instead use "bot.hawkEye.oneShot(target, weapon)" bot.hawkEye.autoAttack(target, weapon) // If you force stop attack use: diff --git a/examples/python/chatterbox.py b/examples/python/chatterbox.py index 3cd51951e..094251300 100644 --- a/examples/python/chatterbox.py +++ b/examples/python/chatterbox.py @@ -10,7 +10,7 @@ # may flood the chat, feel free to check them out for other purposes though. # # This bot also replies to some specific chat messages so you can ask him -# a few informations while you are in game. +# a few information while you are in game. # =========================================================================== import sys, re from javascript import require, On, Once, console diff --git a/lib/plugins/chat.js b/lib/plugins/chat.js index 313258209..9522223c6 100644 --- a/lib/plugins/chat.js +++ b/lib/plugins/chat.js @@ -196,7 +196,7 @@ function inject (bot, options) { bot.tabComplete = tabComplete function addDefaultPatterns () { - // 1.19 changes the chat format to move prefix from message contents to a seperate field. + // 1.19 changes the chat format to move prefix from message contents to a separate field. // TODO: new chat lister to handle this if (!defaultChatPatterns) return bot.addChatPattern('whisper', new RegExp(`^${USERNAME_REGEX} whispers(?: to you)?:? (.*)$`), { deprecated: true }) diff --git a/test/externalTests/placeEntity.js b/test/externalTests/placeEntity.js index c88b92a9f..acbfafebd 100644 --- a/test/externalTests/placeEntity.js +++ b/test/externalTests/placeEntity.js @@ -16,7 +16,7 @@ module.exports = (version) => { addTest('place crystal', async (bot) => { if (!bot.registry.itemsByName.end_crystal) return // unsupported await bot.test.setBlock({ z: 1, relative: true, blockName: 'obsidian' }) - await bot.test.awaitItemRecieved(`/give ${bot.username} end_crystal`) + await bot.test.awaitItemReceived(`/give ${bot.username} end_crystal`) const crystal = await bot.placeEntity(bot.blockAt(bot.entity.position.offset(0, 0, 1)), new Vec3(0, 1, 0)) assert(crystal !== null) let name = 'EnderCrystal' @@ -45,7 +45,7 @@ module.exports = (version) => { } await placeBlocksForTest('water') - await bot.test.awaitItemRecieved(`/give ${bot.username} ${bot.registry.oak_boat ? 'oak_boat' : 'boat'}`) + await bot.test.awaitItemReceived(`/give ${bot.username} ${bot.registry.oak_boat ? 'oak_boat' : 'boat'}`) const boat = await bot.placeEntity(bot.blockAt(bot.entity.position.offset(0, -1, -2)), new Vec3(0, -1, 0)) assert(boat !== null) const name = bot.supportFeature('entityNameUpperCaseNoUnderscore') ? 'Boat' : 'boat' @@ -69,7 +69,7 @@ module.exports = (version) => { } else { command = '/give @p zombie_spawn_egg 1' // >1.12 } - await bot.test.awaitItemRecieved(command) + await bot.test.awaitItemReceived(command) const zombie = await bot.placeEntity(bot.blockAt(bot.entity.position.offset(0, 0, 1)), new Vec3(0, 1, 0)) assert(zombie !== null) const name = bot.supportFeature('entityNameUpperCaseNoUnderscore') ? 'Zombie' : 'zombie' @@ -80,7 +80,7 @@ module.exports = (version) => { }) addTest('place armor stand', async (bot) => { - await bot.test.awaitItemRecieved(`/give ${bot.username} armor_stand`) + await bot.test.awaitItemReceived(`/give ${bot.username} armor_stand`) const armorStand = await bot.placeEntity(bot.blockAt(bot.entity.position.offset(0, 0, 1)), new Vec3(0, 1, 0)) assert(armorStand !== null) let name diff --git a/test/externalTests/plugins/testCommon.js b/test/externalTests/plugins/testCommon.js index c93b3547f..50cf20c59 100644 --- a/test/externalTests/plugins/testCommon.js +++ b/test/externalTests/plugins/testCommon.js @@ -28,7 +28,7 @@ function inject (bot) { return new Promise((resolve) => { setTimeout(resolve, ms) }) } - bot.test.awaitItemRecieved = async (command) => { + bot.test.awaitItemReceived = async (command) => { const p = once(bot.inventory, 'updateSlot') bot.chat(command) await p // await getting the item From eb9982aa04973b0086aac68a2847005d77f01a3d Mon Sep 17 00:00:00 2001 From: Alex Howe Date: Sun, 16 Jun 2024 22:22:54 +0100 Subject: [PATCH 02/33] Added support for 1.18+ edit book packet #3204 (#3373) * Added support for new edit book packet (#3204) * Ran linter * Pass the slot to editBook * Change slot to different parameter to work with old hand value * Update book.js use new feature --------- Co-authored-by: extremeheat --- lib/plugins/book.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/plugins/book.js b/lib/plugins/book.js index 56fb69bfb..9c1616979 100644 --- a/lib/plugins/book.js +++ b/lib/plugins/book.js @@ -10,17 +10,27 @@ function inject (bot) { if (bot.supportFeature('editBookIsPluginChannel')) { bot._client.registerChannel('MC|BEdit', 'slot') bot._client.registerChannel('MC|BSign', 'slot') - editBook = (book, signing = false) => { + editBook = (book, pages, title, slot, signing = false) => { if (signing) bot._client.writeChannel('MC|BSign', Item.toNotch(book)) else bot._client.writeChannel('MC|BEdit', Item.toNotch(book)) } } else if (bot.supportFeature('hasEditBookPacket')) { - editBook = (book, signing = false, hand = 0) => { - bot._client.write('edit_book', { - new_book: Item.toNotch(book), - signing, - hand - }) + if (bot.supportFeature('editBookPacketUsesNbt')) { // 1.13 - 1.17 + editBook = (book, pages, title, slot, signing = false, hand = 0) => { + bot._client.write('edit_book', { + hand: slot, + pages, + title + }) + } + } else { // 1.18+ + editBook = (book, pages, title, slot, signing = false, hand = 0) => { + bot._client.write('edit_book', { + new_book: Item.toNotch(book), + signing, + hand + }) + } } } @@ -38,7 +48,7 @@ function inject (bot) { bot.setQuickBarSlot(moveToQuickBar ? 0 : slot - 36) const modifiedBook = await modifyBook(moveToQuickBar ? 36 : slot, pages, author, title, signing) - editBook(modifiedBook, signing) + editBook(modifiedBook, pages, title, moveToQuickBar ? 0 : slot - 36, signing) await once(bot.inventory, `updateSlot:${moveToQuickBar ? 36 : slot}`) bot.setQuickBarSlot(quickBarSlot) From ef042a242ca9f5fc5820fe4dc2e1d997ef1db202 Mon Sep 17 00:00:00 2001 From: Cornbread2100 Date: Sat, 13 Jul 2024 17:05:18 -0400 Subject: [PATCH 03/33] Typo fixes (#3418) --- docs/api.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api.md b/docs/api.md index fe8bfc54f..e8ae76d1a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -808,7 +808,7 @@ Create and return an instance of the class bot. * checkTimeoutInterval : default to `30*1000` (30s), check if keepalive received at that period, disconnect otherwise. * loadInternalPlugins : defaults to true * storageBuilder : an optional function, takes as argument version and worldName and return an instance of something with the same API as prismarine-provider-anvil. Will be used to save the world. - * client : an instance of node-minecraft-protocol, if not specified, mineflayer makes it's own client. This can be used to enable using mineflayer through a proxy of many clients or a vanilla client and a mineflayer client. + * client : an instance of node-minecraft-protocol, if not specified, mineflayer makes its own client. This can be used to enable using mineflayer through a proxy of many clients or a vanilla client and a mineflayer client. * brand : the brand name for the client to use. Defaults to vanilla. Can be used to simulate custom clients for servers that require it. * respawn : when set to false disables bot from automatically respawning, defaults to true. * plugins : object : defaults to {} @@ -1892,7 +1892,7 @@ dig any other blocks until the block has been broken, or you call `bot.stopDigging()`. * `block` - the block to start digging into - * `forceLook` - (optional) if true, look at the block and start mining instantly. If false, the bot will slowly turn to the block to mine. Additionally, this can be assigned to 'ignore' to prevent the bot from moving it's head at all. Also, this can be assigned to 'raycast' to raycast from the bots head to place where the bot is looking. + * `forceLook` - (optional) if true, look at the block and start mining instantly. If false, the bot will slowly turn to the block to mine. Additionally, this can be assigned to 'ignore' to prevent the bot from moving its head at all. Also, this can be assigned to 'raycast' to raycast from the bots head to place where the bot is looking. * `digFace` - (optional) Default is 'auto' looks at the center of the block and mines the top face. Can also be a vec3 vector of the face the bot should be looking at when digging the block. For example: ```vec3(0, 1, 0)``` when mining the top. Can also be 'raycast' raycast checks if there is a face visible by the bot and mines that face. Useful for servers with anti cheat. @@ -2249,4 +2249,4 @@ Note that while flying, `bot.entity.velocity` will not be accurate. #### bot.creative.stopFlying() -Restores `bot.physics.gravity` to it's original value. +Restores `bot.physics.gravity` to its original value. From 78b4eccb4572a821b11c3124b7a593f3b91f1180 Mon Sep 17 00:00:00 2001 From: AreaDenial <32404366+AreaDenial@users.noreply.github.com> Date: Sat, 13 Jul 2024 17:06:39 -0400 Subject: [PATCH 04/33] Player hitbox fixes (#3382) * Update entities.js * Update ray_trace.js Replace raytrace start position with bot.entity.eyeHeight Remove old fix for player hitbox height * Update digging.js Set raytrace start position to bot.entity.eyeHeight * Update index.js Minor typo in comment lol * Update physics.js Change lookAt start position to use bot.entity.eyeHeight * Update blocks.js Change canSeeBlock to use bot.entity.eyeHeight * Update entities.js Update player height on crouch/uncrouch * Update entities.js Initialize bot.entity.height and bot.entity.width on login * Update entities.js Typo * Update entities.js Initialize bot entity eyeHeight on login --- examples/place_end_crystal/index.js | 2 +- lib/plugins/blocks.js | 2 +- lib/plugins/digging.js | 10 +++++----- lib/plugins/entities.js | 22 ++++++++++++++++------ lib/plugins/physics.js | 6 ++++-- lib/plugins/ray_trace.js | 4 ++-- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/examples/place_end_crystal/index.js b/examples/place_end_crystal/index.js index 8fce0abf2..5a853193a 100644 --- a/examples/place_end_crystal/index.js +++ b/examples/place_end_crystal/index.js @@ -51,7 +51,7 @@ bot.on('chat', async (ign, msg) => { const { x: aboveX, y: aboveY, z: aboveZ } = block.position.offset(0, 1, 0) const blockBoundingBox = new AABB(aboveX, aboveY, aboveZ, aboveX + 1, aboveY + 2, aboveZ + 1) const entityAABBs = Object.values(bot.entities).map(entity => { - // taken from taken from https://github.com/PrismarineJS/prismarine-physics/blob/d145e54a4bb8604300258badd7563f59f2101922/index.js#L92 + // taken from https://github.com/PrismarineJS/prismarine-physics/blob/d145e54a4bb8604300258badd7563f59f2101922/index.js#L92 const w = entity.height / 2 const { x, y, z } = entity.position return new AABB(-w, 0, -w, w, entity.height, w).offset(x, y, z) diff --git a/lib/plugins/blocks.js b/lib/plugins/blocks.js index a92e7f858..1c2134081 100644 --- a/lib/plugins/blocks.js +++ b/lib/plugins/blocks.js @@ -221,7 +221,7 @@ function inject (bot, { version, storageBuilder, hideErrors }) { // if passed in block is within line of sight to the bot, returns true // also works on anything with a position value function canSeeBlock (block) { - const headPos = bot.entity.position.offset(0, bot.entity.height, 0) + const headPos = bot.entity.position.offset(0, bot.entity.eyeHeight, 0) const range = headPos.distanceTo(block.position) const dir = block.position.offset(0.5, 0.5, 0.5).minus(headPos) const match = (inputBlock, iter) => { diff --git a/lib/plugins/digging.js b/lib/plugins/digging.js index ea2763b05..39d3efbc5 100644 --- a/lib/plugins/digging.js +++ b/lib/plugins/digging.js @@ -42,10 +42,10 @@ function inject (bot) { ) } else if (digFace === 'raycast') { // Check faces that could be seen from the current position. If the delta is smaller then 0.5 that means the - // bot cam most likely not see the face as the block is 1 block thick - // this could be false for blocks that have a smaller bounding box then 1x1x1 + // bot can most likely not see the face as the block is 1 block thick + // this could be false for blocks that have a smaller bounding box than 1x1x1 const dx = bot.entity.position.x - (block.position.x + 0.5) - const dy = bot.entity.position.y + bot.entity.height - (block.position.y + 0.5) + const dy = bot.entity.position.y + bot.entity.eyeHeight - (block.position.y + 0.5) const dz = bot.entity.position.z - (block.position.z + 0.5) // Check y first then x and z const visibleFaces = { @@ -63,7 +63,7 @@ function inject (bot) { 0.5 + (i === 'y' ? visibleFaces[i] * 0.5 : 0), 0.5 + (i === 'z' ? visibleFaces[i] * 0.5 : 0) ) - const startPos = bot.entity.position.offset(0, bot.entity.height, 0) + const startPos = bot.entity.position.offset(0, bot.entity.eyeHeight, 0) const rayBlock = bot.world.raycast(startPos, targetPos.clone().subtract(startPos).normalize(), 5) if (rayBlock) { if (startPos.distanceTo(rayBlock.intersect) < startPos.distanceTo(targetPos)) { @@ -94,7 +94,7 @@ function inject (bot) { for (const i in validFaces) { const tPos = validFaces[i].targetPos const cDist = new Vec3(tPos.x, tPos.y, tPos.z).distanceSquared( - bot.entity.position.offset(0, bot.entity.height, 0) + bot.entity.position.offset(0, bot.entity.eyeHeight, 0) ) if (distSqrt > cDist) { closest = validFaces[i] diff --git a/lib/plugins/entities.js b/lib/plugins/entities.js index e5eb918f4..943158848 100644 --- a/lib/plugins/entities.js +++ b/lib/plugins/entities.js @@ -1,8 +1,12 @@ const { Vec3 } = require('vec3') const conv = require('../conversions') -const NAMED_ENTITY_HEIGHT = 1.62 -const NAMED_ENTITY_WIDTH = 0.6 -const CROUCH_HEIGHT = NAMED_ENTITY_HEIGHT - 0.08 +// These values are only accurate for versions 1.14 and above (crouch hitbox changes) +// Todo: hitbox sizes for sleeping, swimming/crawling, and flying with elytra +const PLAYER_HEIGHT = 1.8 +const CROUCH_HEIGHT = 1.5 +const PLAYER_WIDTH = 0.6 +const PLAYER_EYEHEIGHT = 1.62 +const CROUCH_EYEHEIGHT = 1.27 module.exports = inject @@ -102,6 +106,9 @@ function inject (bot) { bot.entity.username = bot._client.username bot.entity.type = 'player' bot.entity.name = 'player' + bot.entity.height = PLAYER_HEIGHT + bot.entity.width = PLAYER_WIDTH + bot.entity.eyeHeight = PLAYER_EYEHEIGHT }) bot._client.on('entity_equipment', (packet) => { @@ -130,11 +137,13 @@ function inject (bot) { }) bot.on('entityCrouch', (entity) => { + entity.eyeHeight = CROUCH_EYEHEIGHT entity.height = CROUCH_HEIGHT }) bot.on('entityUncrouch', (entity) => { - entity.height = NAMED_ENTITY_HEIGHT + entity.eyeHeight = PLAYER_EYEHEIGHT + entity.height = PLAYER_HEIGHT }) bot._client.on('collect', (packet) => { @@ -184,8 +193,9 @@ function inject (bot) { entity.username = bot.uuidToUsername[uuid] entity.uuid = uuid updateEntityPos(entity, pos) - entity.height = NAMED_ENTITY_HEIGHT - entity.width = NAMED_ENTITY_WIDTH + entity.eyeHeight = PLAYER_EYEHEIGHT + entity.height = PLAYER_HEIGHT + entity.width = PLAYER_WIDTH if (bot.players[entity.username] !== undefined && !bot.players[entity.username].entity) { bot.players[entity.username].entity = entity } diff --git a/lib/plugins/physics.js b/lib/plugins/physics.js index 79fe868bf..578e5a529 100644 --- a/lib/plugins/physics.js +++ b/lib/plugins/physics.js @@ -323,7 +323,7 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) { } bot.lookAt = async (point, force) => { - const delta = point.minus(bot.entity.position.offset(0, bot.entity.height, 0)) + const delta = point.minus(bot.entity.position.offset(0, bot.entity.eyeHeight, 0)) const yaw = Math.atan2(-delta.x, -delta.z) const groundDistance = Math.sqrt(delta.x * delta.x + delta.z * delta.z) const pitch = Math.atan2(delta.y, groundDistance) @@ -332,7 +332,9 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) { // player position and look (clientbound) bot._client.on('position', (packet) => { - bot.entity.height = 1.62 + // Is this necessary? Feels like it might wrongly overwrite hitbox size sometimes + // e.g. when crouching/crawling/swimming. Can someone confirm? + bot.entity.height = 1.8 // Velocity is only set to 0 if the flag is not set, otherwise keep current velocity const vel = bot.entity.velocity diff --git a/lib/plugins/ray_trace.js b/lib/plugins/ray_trace.js index 685d190fc..e26034caa 100644 --- a/lib/plugins/ray_trace.js +++ b/lib/plugins/ray_trace.js @@ -28,7 +28,7 @@ module.exports = (bot) => { .filter(entity => entity.type !== 'object' && entity.username !== bot.username && entity.position.distanceTo(bot.entity.position) <= maxDistance) const dir = new Vec3(-Math.sin(bot.entity.yaw) * Math.cos(bot.entity.pitch), Math.sin(bot.entity.pitch), -Math.cos(bot.entity.yaw) * Math.cos(bot.entity.pitch)) - const iterator = new RaycastIterator(bot.entity.position.offset(0, bot.entity.height, 0), dir.normalize(), maxDistance) + const iterator = new RaycastIterator(bot.entity.position.offset(0, bot.entity.eyeHeight, 0), dir.normalize(), maxDistance) let targetEntity = null let targetDist = maxDistance @@ -37,7 +37,7 @@ module.exports = (bot) => { const entity = entities[i] const w = entity.width / 2 - const shapes = [[-w, 0, -w, w, entity.height + (entity.type === 'player' ? 0.18 : 0), w]] + const shapes = [[-w, 0, -w, w, entity.height, w]] const intersect = iterator.intersect(shapes, entity.position) if (intersect) { const entityDir = entity.position.minus(bot.entity.position) // Can be combined into 1 line From 5d39db26a6ab17baac38b68af8ccd3eeb4af3def Mon Sep 17 00:00:00 2001 From: DenisKvak1 <59225772+DenisKvak1@users.noreply.github.com> Date: Mon, 29 Jul 2024 00:11:52 +0300 Subject: [PATCH 05/33] Fix updateSlot event type (#3425) --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 759747614..d2f172143 100644 --- a/index.d.ts +++ b/index.d.ts @@ -649,7 +649,7 @@ export class Painting { interface StorageEvents { open: () => void close: () => void - updateSlot: (oldItem: Item | null, newItem: Item | null) => void + updateSlot: (slot: number, oldItem: Item | null, newItem: Item | null) => void } interface FurnaceEvents extends StorageEvents { From 1d461616b514969fdece38e49bfbec747ab8d76a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 02:51:11 -0400 Subject: [PATCH 06/33] Bump @types/node from 20.14.14 to 22.1.0 (#3431) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fc6a5714f..4aae690b5 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "vec3": "^0.1.7" }, "devDependencies": { - "@types/node": "^20.2.1", + "@types/node": "^22.1.0", "doctoc": "^2.0.1", "minecraft-wrap": "^1.3.0", "mineflayer": "file:.", From d00c386cfe51cefc361c0ff4d30b100aee9f114a Mon Sep 17 00:00:00 2001 From: DenisKvak1 <59225772+DenisKvak1@users.noreply.github.com> Date: Tue, 20 Aug 2024 21:15:35 +0300 Subject: [PATCH 07/33] Fixed a bug with not closing the window when changing the subserver (#3424) --- lib/plugins/inventory.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/plugins/inventory.js b/lib/plugins/inventory.js index 497021841..0277f3fef 100644 --- a/lib/plugins/inventory.js +++ b/lib/plugins/inventory.js @@ -691,6 +691,13 @@ function inject (bot, { hideErrors }) { bot.currentWindow = null bot.emit('windowClose', oldWindow) }) + bot._client.on('login', () => { + // close window when switch subserver + const oldWindow = bot.currentWindow + if (!oldWindow) return + bot.currentWindow = null + bot.emit('windowClose', oldWindow) + }) bot._client.on('set_slot', (packet) => { // set slot const window = packet.windowId === 0 ? bot.inventory : bot.currentWindow From 05b48ad0dad4cf64a1c11660bac256d7b4015841 Mon Sep 17 00:00:00 2001 From: Rocco A <43506958+GenerelSchwerz@users.noreply.github.com> Date: Sun, 8 Sep 2024 00:47:37 -0400 Subject: [PATCH 08/33] types: add pitchSpeed as an option in typings (#3446) --- index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/index.d.ts b/index.d.ts index d2f172143..cef39b685 100644 --- a/index.d.ts +++ b/index.d.ts @@ -541,6 +541,7 @@ export interface PhysicsOptions { playerHeight: number jumpSpeed: number yawSpeed: number + pitchSpeed: number sprintSpeed: number maxGroundSpeedSoulSand: number maxGroundSpeedWater: number From 1c2a5c0fa78f74a63fabd7edde85c4a74db32dee Mon Sep 17 00:00:00 2001 From: Silke pilon Date: Sun, 8 Sep 2024 22:08:58 +0200 Subject: [PATCH 09/33] Update README.md (#3420) remove --depth flag as it no longer works. npm warn update The --depth option no longer has any effect. See RFC0019. npm warn update https://github.com/npm/rfcs/blob/latest/implemented/0019-remove-update-depth-option.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 1888ce4d1..11dbea462 100644 --- a/docs/README.md +++ b/docs/README.md @@ -43,7 +43,7 @@ npm install mineflayer To update mineflayer (or any Node.js) package and its dependencies, use ```bash -npm update --depth 9999 +npm update ``` ## Documentation From 44fad41c18be5024564e24e1cdee624d35d4d387 Mon Sep 17 00:00:00 2001 From: extremeheat Date: Sat, 12 Oct 2024 22:22:39 -0400 Subject: [PATCH 10/33] 1.20.6 (#3412) * 1.20.5 * lint * fix * update registry * update java * add debug * fix * debug * Update testCommon.js * update ci * add protodef AOT compile code * Update protodefCompile.js * Update testCommon.js * pitem, fix `login` event * debug * Update testCommon.js * Update package.json test * Update package.json * update test events * Update testCommon.js * update * fix pitem source * remove some debug logging * update * Fix trade test * remove manual protodef install from ci * remove unnecessary install in ci * change back version.js to support latest version * update deps * remove compile protocol in ci * bump to support 1.20.6 * in tests teleport to overworld to prevent nether test to break the environment * fix respawn in internal tests * use normal tp before 1.14 * lint * fix held item test * fix item drop internal test for 1.20.6 * cleanup --------- Co-authored-by: Romain Beaumont --- .github/workflows/ci.yml | 2 +- .gitignore | 1 + .gitpod.yml | 2 +- docs/README.md | 2 +- lib/loader.js | 1 + lib/particle.js | 30 +++++++++++----- lib/plugins/blocks.js | 8 +++++ lib/plugins/breath.js | 22 ++++++------ lib/plugins/creative.js | 5 ++- lib/plugins/entities.js | 6 ++++ lib/plugins/fishing.js | 9 +++-- lib/plugins/game.js | 42 +++++++++++++--------- lib/version.js | 2 +- package.json | 13 +++---- test/externalTest.js | 2 +- test/externalTests/heldItem.js | 1 + test/externalTests/nether.js | 4 ++- test/externalTests/particles.js | 6 +++- test/externalTests/plugins/testCommon.js | 35 ++++++++++++++----- test/externalTests/trade.js | 44 +++++++++++++++--------- test/externalTests/useChests.js | 6 ++-- test/internalTest.js | 22 ++++++++++-- 22 files changed, 184 insertions(+), 81 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dbcffcf15..e307beace 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: - name: Setup Java JDK uses: actions/setup-java@v1.4.3 with: - java-version: 17 + java-version: 21 java-package: jre - name: Install Dependencies run: npm install diff --git a/.gitignore b/.gitignore index c76d8b46f..008972684 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ test/server_* .vscode .DS_Store launcher_accounts.json +data \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml index 38fc373b5..13f366c56 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,2 +1,2 @@ tasks: -- command: npm install +- command: npm install && sdk install java diff --git a/docs/README.md b/docs/README.md index 11dbea462..2569ccbbf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,7 +17,7 @@ First time using Node.js? You may want to start with the [tutorial](tutorial.md) ## Features - * Supports Minecraft 1.8 to 1.20.4 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19 and 1.20) + * Supports Minecraft 1.8 to 1.20.5 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19 and 1.20 upto 1.20.6) * Entity knowledge and tracking. * Block knowledge. You can query the world around you. Milliseconds to find any block. * Physics and movement - handle all bounding boxes diff --git a/lib/loader.js b/lib/loader.js index 4b260229d..1dc831711 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -95,6 +95,7 @@ function createBot (options = {}) { }).map(key => options.plugins[key]) bot.loadPlugins([...internalPlugins, ...externalPlugins]) + options.validateChannelProtocol = false bot._client = bot._client ?? mc.createClient(options) bot._client.on('connect', () => { bot.emit('connect') diff --git a/lib/particle.js b/lib/particle.js index 1a7c82814..36a72d80f 100644 --- a/lib/particle.js +++ b/lib/particle.js @@ -5,8 +5,8 @@ module.exports = loader function loader (registry) { class Particle { constructor (id, position, offset, count = 1, movementSpeed = 0, longDistanceRender = false) { - Object.assign(this, registry.particles[id]) this.id = id + Object.assign(this, registry.particles[id] || registry.particlesByName[id]) this.position = position this.offset = offset this.count = count @@ -15,14 +15,26 @@ function loader (registry) { } static fromNetwork (packet) { - return new Particle( - packet.particleId, - new Vec3(packet.x, packet.y, packet.z), - new Vec3(packet.offsetX, packet.offsetY, packet.offsetZ), - packet.particles, - packet.particleData, - packet.longDistance - ) + if (registry.supportFeature('updatedParticlesPacket')) { + // TODO: We add extra data that's inside packet.particle.data that varies by the particle's .type + return new Particle( + packet.particle.type, + new Vec3(packet.x, packet.y, packet.z), + new Vec3(packet.offsetX, packet.offsetY, packet.offsetZ), + packet.amount, + packet.velocityOffset, + packet.longDistance + ) + } else { + return new Particle( + packet.particleId, + new Vec3(packet.x, packet.y, packet.z), + new Vec3(packet.offsetX, packet.offsetY, packet.offsetZ), + packet.particles, + packet.particleData, + packet.longDistance + ) + } } } diff --git a/lib/plugins/blocks.js b/lib/plugins/blocks.js index 1c2134081..6a9c1655a 100644 --- a/lib/plugins/blocks.js +++ b/lib/plugins/blocks.js @@ -531,6 +531,9 @@ function inject (bot, { version, storageBuilder, hideErrors }) { if (bot.supportFeature('dimensionIsAnInt')) { dimension = packet.dimension worldName = dimensionToFolderName(dimension) + } else if (bot.supportFeature('spawnRespawnWorldDataField')) { // 1.20.5+ + dimension = packet.worldState.dimension + worldName = packet.worldState.name } else { dimension = packet.dimension worldName = /^minecraft:.+/.test(packet.worldName) ? packet.worldName : `minecraft:${packet.worldName}` @@ -542,6 +545,11 @@ function inject (bot, { version, storageBuilder, hideErrors }) { if (bot.supportFeature('dimensionIsAnInt')) { // <=1.15.2 if (dimension === packet.dimension) return dimension = packet.dimension + } else if (bot.supportFeature('spawnRespawnWorldDataField')) { // 1.20.5+ + if (dimension === packet.worldState.dimension) return + if (worldName === packet.worldState.name && packet.copyMetadata === true) return // don't unload chunks if in same world and metaData is true + dimension = packet.worldState.dimension + worldName = packet.worldState.name } else { // >= 1.15.2 if (dimension === packet.dimension) return if (worldName === packet.worldName && packet.copyMetadata === true) return // don't unload chunks if in same world and metaData is true diff --git a/lib/plugins/breath.js b/lib/plugins/breath.js index a112b99fc..7ac316824 100644 --- a/lib/plugins/breath.js +++ b/lib/plugins/breath.js @@ -1,17 +1,19 @@ module.exports = inject function inject (bot) { + if (bot.supportFeature('mcDataHasEntityMetadata')) { + // this is handled inside entities.js. We don't yet have entity metadataKeys for all versions but once we do + // we can delete the numerical checks here and in entities.js https://github.com/extremeheat/mineflayer/blob/eb9982aa04973b0086aac68a2847005d77f01a3d/lib/plugins/entities.js#L469 + return + } bot._client.on('entity_metadata', (packet) => { - if (!bot?.entity?.id === packet?.entityId) return - if (packet?.metadata[1]?.key === 1) { - if (!packet?.metadata[1]?.value) return - bot.oxygenLevel = Math.round(packet.metadata[1].value / 15) - bot.emit('breath') - } - if (packet?.metadata[0]?.key === 1) { - if (!packet?.metadata[0]?.value) return - bot.oxygenLevel = Math.round(packet.metadata[0].value / 15) - bot.emit('breath') + if (!bot.entity) return + if (bot.entity.id !== packet.entityId) return + for (const metadata of packet.metadata) { + if (metadata.key === 1) { + bot.oxygenLevel = Math.round(packet.metadata[1].value / 15) + bot.emit('breath') + } } }) } diff --git a/lib/plugins/creative.js b/lib/plugins/creative.js index 33c27305b..573f581ee 100644 --- a/lib/plugins/creative.js +++ b/lib/plugins/creative.js @@ -35,7 +35,10 @@ function inject (bot) { item: Item.toNotch(item) }) - await onceWithCleanup(bot.inventory, `updateSlot:${slot}`, { checkCondition: (oldItem, newItem) => item === null ? newItem === null : newItem?.name === item.name && newItem?.count === item.count && newItem?.metadata === item.metadata }) + await onceWithCleanup(bot.inventory, `updateSlot:${slot}`, { + timeout: 5000, + checkCondition: (oldItem, newItem) => item === null ? newItem === null : newItem?.name === item.name && newItem?.count === item.count && newItem?.metadata === item.metadata + }) creativeSlotsUpdates[slot] = false } diff --git a/lib/plugins/entities.js b/lib/plugins/entities.js index 943158848..49a5b8696 100644 --- a/lib/plugins/entities.js +++ b/lib/plugins/entities.js @@ -470,6 +470,12 @@ function inject (bot) { bot.emit('entityUncrouch', entity) } } + + // Breathing (formerly in breath.js) + if (metas.air_supply != null) { + bot.oxygenLevel = Math.round(metas.air_supply / 15) + bot.emit('breath') + } } else { const typeSlot = (bot.supportFeature('itemsAreAlsoBlocks') ? 5 : 6) + (bot.supportFeature('entityMetadataHasLong') ? 1 : 0) const slot = packet.metadata.find(e => e.type === typeSlot) diff --git a/lib/plugins/fishing.js b/lib/plugins/fishing.js index 3a965a61d..325be1215 100644 --- a/lib/plugins/fishing.js +++ b/lib/plugins/fishing.js @@ -25,8 +25,13 @@ function inject (bot) { if (!lastBobber || fishingTask.done) return const pos = lastBobber.position - const parts = bot.registry.particlesByName - if (packet.particleId === (parts?.fishing ?? parts.bubble).id && packet.particles === 6 && pos.distanceTo(new Vec3(packet.x, pos.y, packet.z)) <= 1.23) { + + const bobberCondition = bot.registry.supportFeature('updatedParticlesPacket') + ? ((packet.particle.type === 'fishing' || packet.particle.type === 'bubble') && packet.amount === 6 && pos.distanceTo(new Vec3(packet.x, pos.y, packet.z)) <= 1.23) + // This "(particles.fishing ?? particles.bubble).id" condition doesn't make sense (these are both valid types) + : (packet.particleId === (bot.registry.particlesByName.fishing ?? bot.registry.particlesByName.bubble).id && packet.particles === 6 && pos.distanceTo(new Vec3(packet.x, pos.y, packet.z)) <= 1.23) + + if (bobberCondition) { bot.activateItem() lastBobber = undefined fishingTask.finish() diff --git a/lib/plugins/game.js b/lib/plugins/game.js index 4006d145f..b9dedef2d 100644 --- a/lib/plugins/game.js +++ b/lib/plugins/game.js @@ -25,8 +25,14 @@ function inject (bot, options) { function handleRespawnPacketData (packet) { bot.game.levelType = packet.levelType ?? (packet.isFlat ? 'flat' : 'default') bot.game.hardcore = packet.isHardcore ?? Boolean(packet.gameMode & 0b100) - bot.game.gameMode = parseGameMode(packet.gameMode) - if (bot.supportFeature('dimensionIsAnInt')) { + bot.game.gameMode = packet.gamemode || parseGameMode(packet.gameMode) + if (bot.supportFeature('segmentedRegistryCodecData')) { // 1.20.5 + if (typeof packet.dimension === 'number') { + bot.game.dimension = bot.registry.dimensionsArray[packet.dimension]?.name?.replace('minecraft:', '') + } else if (typeof packet.dimension === 'string') { // iirc, in 1.21 it's back to a string + bot.game.dimension = packet.dimension.replace('minecraft:', '') + } + } else if (bot.supportFeature('dimensionIsAnInt')) { bot.game.dimension = dimensionNames[packet.dimension] } else if (bot.supportFeature('dimensionIsAString')) { bot.game.dimension = packet.dimension.replace('minecraft:', '') @@ -40,25 +46,25 @@ function inject (bot, options) { bot.registry.loadDimensionCodec(packet.dimensionCodec) } + bot.game.minY = 0 + bot.game.height = 256 + if (bot.supportFeature('dimensionDataInCodec')) { // 1.19+ - if (packet.worldType) { // login + // pre 1.20.5 before we consolidated login and respawn's SpawnInfo structure into one type, + // "dimension" was called "worldType" in login_packet's payload but not respawn. + if (packet.worldType && !bot.game.dimension) { bot.game.dimension = packet.worldType.replace('minecraft:', '') - const { minY, height } = bot.registry.dimensionsByName[bot.game.dimension] - bot.game.minY = minY - bot.game.height = height - } else if (packet.dimension) { // respawn - bot.game.dimension = packet.dimension.replace('minecraft:', '') - const { minY, height } = bot.registry.dimensionsByName[bot.game.dimension] - bot.game.minY = minY - bot.game.height = height + } + console.log('*Dimension data', bot.game.dimension, bot.registry.dimensionsByName, packet) + const dimData = bot.registry.dimensionsByName[bot.game.dimension] + if (dimData) { + bot.game.minY = dimData.minY + bot.game.height = dimData.height } } else if (bot.supportFeature('dimensionDataIsAvailable')) { // 1.18 const dimensionData = nbt.simplify(packet.dimension) bot.game.minY = dimensionData.min_y bot.game.height = dimensionData.height - } else { - bot.game.minY = 0 - bot.game.height = 256 } if (packet.difficulty) { @@ -73,11 +79,12 @@ function inject (bot, options) { // 1.20.2 bot._client.on('registry_data', (packet) => { - bot.registry.loadDimensionCodec(packet.codec) + console.log('Loading Dimension Codec', JSON.stringify(packet).slice(0, 100)) + bot.registry.loadDimensionCodec(packet.codec || packet) }) bot._client.on('login', (packet) => { - handleRespawnPacketData(packet) + handleRespawnPacketData(packet.worldState || packet) bot.game.maxPlayers = packet.maxPlayers if (packet.enableRespawnScreen) { @@ -95,7 +102,8 @@ function inject (bot, options) { }) bot._client.on('respawn', (packet) => { - handleRespawnPacketData(packet) + // in 1.20.5+ protocol we move the shared spawn data into one SpawnInfo type under .worldState + handleRespawnPacketData(packet.worldState || packet) bot.emit('game') }) diff --git a/lib/version.js b/lib/version.js index b5306c667..755db7285 100644 --- a/lib/version.js +++ b/lib/version.js @@ -1,4 +1,4 @@ -const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2', '1.20.4'] +const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2', '1.20.4', '1.20.6'] module.exports = { testedVersions, diff --git a/package.json b/package.json index 4aae690b5..b805cadb3 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "types": "index.d.ts", "scripts": { - "mocha_test": "mocha --reporter spec --exit", + "mocha_test": "mocha --reporter spec --exit --bail", "test": "npm run mocha_test", "pretest": "npm run lint", "lint": "standard && standard-markdown", @@ -21,21 +21,21 @@ }, "license": "MIT", "dependencies": { - "minecraft-data": "^3.56.0", - "minecraft-protocol": "^1.47.0", + "minecraft-data": "^3.76.0", + "minecraft-protocol": "^1.49.0", "prismarine-biome": "^1.1.1", "prismarine-block": "^1.17.0", "prismarine-chat": "^1.7.1", "prismarine-chunk": "^1.34.0", "prismarine-entity": "^2.3.0", - "prismarine-item": "^1.14.0", + "prismarine-item": "^1.15.0", "prismarine-nbt": "^2.0.0", "prismarine-physics": "^1.8.0", "prismarine-recipe": "^1.3.0", - "prismarine-registry": "^1.5.0", + "prismarine-registry": "^1.8.0", "prismarine-windows": "^2.9.0", "prismarine-world": "^3.6.0", - "protodef": "^1.14.0", + "protodef": "1.17.0", "typed-emitter": "^1.0.0", "vec3": "^0.1.7" }, @@ -45,6 +45,7 @@ "minecraft-wrap": "^1.3.0", "mineflayer": "file:.", "mocha": "^10.0.0", + "protodef-yaml": "^1.5.3", "standard": "^17.0.0", "standard-markdown": "^7.1.0", "typescript": "^5.4.5" diff --git a/test/externalTest.js b/test/externalTest.js index 02cdbe44e..c6fca04f5 100644 --- a/test/externalTest.js +++ b/test/externalTest.js @@ -132,7 +132,7 @@ for (const supportedVersion of mineflayer.testedVersions) { const runTest = (testName, testFunction) => { return function (done) { this.timeout(TEST_TIMEOUT_MS) - bot.test.sayEverywhere(`starting ${testName}`) + bot.test.sayEverywhere(`### Starting ${testName}`) testFunction(bot, done).then(res => done()).catch(e => done(e)) } } diff --git a/test/externalTests/heldItem.js b/test/externalTests/heldItem.js index 51286db0a..319ae421b 100644 --- a/test/externalTests/heldItem.js +++ b/test/externalTests/heldItem.js @@ -5,6 +5,7 @@ module.exports = () => async (bot) => { await bot.test.becomeCreative() await bot.test.clearInventory() + await bot.test.wait(100) assert.equal(bot.heldItem, null) const stoneId = bot.registry.itemsByName.stone.id diff --git a/test/externalTests/nether.js b/test/externalTests/nether.js index c2560b302..20f7a2c45 100644 --- a/test/externalTests/nether.js +++ b/test/externalTests/nether.js @@ -14,12 +14,14 @@ module.exports = () => async (bot) => { const p = new Promise((resolve, reject) => { bot._client.on('open_sign_entity', (packet) => { + console.log('Open sign', packet) const sign = bot.blockAt(new Vec3(packet.location)) bot.updateSign(sign, '1\n2\n3\n') setTimeout(() => { // Get updated sign const sign = bot.blockAt(bot.entity.position) + console.log('Updated sign', sign) assert.strictEqual(sign.signText.trimEnd(), '1\n2\n3') @@ -48,5 +50,5 @@ module.exports = () => async (bot) => { await bot.lookAt(lowerBlock.position, true) await bot.test.setInventorySlot(36, new Item(signItem.id, 1, 0)) await bot.placeBlock(lowerBlock, new Vec3(0, 1, 0)) - return p + await p } diff --git a/test/externalTests/particles.js b/test/externalTests/particles.js index 58fe4c939..06272f243 100644 --- a/test/externalTests/particles.js +++ b/test/externalTests/particles.js @@ -5,7 +5,11 @@ module.exports = () => async (bot) => { return new Promise((resolve, reject) => { function onParticleEvent (particle) { - assert.strictEqual(particle.id, particleData.id) + if (typeof particle.id === 'number') { + assert.strictEqual(particle.id, particleData.id) + } else { + assert.strictEqual(particle.id, particleData.name) + } assert.strictEqual(particle.name, particleData.name) assert.strictEqual(particle.position.x, bot.entity.position.x) assert.strictEqual(particle.position.y, bot.entity.position.y) diff --git a/test/externalTests/plugins/testCommon.js b/test/externalTests/plugins/testCommon.js index 50cf20c59..fa8bdeaa3 100644 --- a/test/externalTests/plugins/testCommon.js +++ b/test/externalTests/plugins/testCommon.js @@ -6,6 +6,7 @@ const process = require('process') const assert = require('assert') const { sleep, onceWithCleanup, withTimeout } = require('../../../lib/promise_utils') +const timeout = 5000 module.exports = inject function inject (bot) { @@ -109,7 +110,10 @@ function inject (bot) { // this function behaves the same whether we start in creative mode or not. // also, creative mode is always allowed for ops, even if server.properties says force-gamemode=true in survival mode. let i = 0 - const msgProm = onceWithCleanup(bot, 'message', { checkCondition: msg => gameModeChangedMessages.includes(msg.translate) && i++ > 0 && bot.game.gameMode === getGM(value) }) + const msgProm = onceWithCleanup(bot, 'message', { + timeout, + checkCondition: msg => gameModeChangedMessages.includes(msg.translate) && i++ > 0 && bot.game.gameMode === getGM(value) + }) // do it three times to ensure that we get feedback bot.chat(`/gamemode ${getGM(value)}`) @@ -119,15 +123,22 @@ function inject (bot) { } async function clearInventory () { - const msgProm = onceWithCleanup(bot, 'message', { checkCondition: msg => msg.translate === 'commands.clear.success.single' || msg.translate === 'commands.clear.success' }) + const msgProm = onceWithCleanup(bot, 'message', { + timeout, + checkCondition: msg => msg.translate === 'commands.clear.success.single' || msg.translate === 'commands.clear.success' + }) bot.chat('/give @a stone 1') - await onceWithCleanup(bot.inventory, 'updateSlot', { checkCondition: (slot, oldItem, newItem) => newItem?.name === 'stone' }) - const inventoryClearedProm = Promise.all(bot.inventory.slots.filter(item => item) - .map(item => onceWithCleanup(bot.inventory, `updateSlot:${item.slot}`, { checkCondition: (oldItem, newItem) => newItem === null }))) + bot.inventory.on('updateSlot', (...e) => { + // console.log('inventory.updateSlot', e) + }) + await onceWithCleanup(bot.inventory, 'updateSlot', { timeout: 1000 * 20, checkCondition: (slot, oldItem, newItem) => newItem?.name === 'stone' }) bot.chat('/clear') // don't rely on the message (as it'll come to early), wait for the result of /clear instead await msgProm // wait for the message so it doesn't leak into chat tests - await inventoryClearedProm - assert.strictEqual(bot.inventory.slots.filter(i => i).length, 0) + + // Check that the inventory is clear + for (const slot of bot.inventory.slots) { + if (slot && slot.itemCount <= 0) throw new Error('Inventory was not cleared: ' + JSON.stringify(bot.inventory.slots)) + } } // you need to be in creative mode for this to work @@ -137,9 +148,13 @@ function inject (bot) { } async function teleport (position) { - bot.chat(`/tp ${bot.username} ${position.x} ${position.y} ${position.z}`) - + if (bot.supportFeature('hasExecuteCommand')) { + bot.test.sayEverywhere(`/execute in overworld run teleport ${bot.username} ${position.x} ${position.y} ${position.z}`) + } else { + bot.test.sayEverywhere(`/tp ${bot.username} ${position.x} ${position.y} ${position.z}`) + } return onceWithCleanup(bot, 'move', { + timeout, checkCondition: () => bot.entity.position.distanceTo(position) < 0.9 }) } @@ -155,6 +170,7 @@ function inject (bot) { async function tellAndListen (to, what, listen) { const chatMessagePromise = onceWithCleanup(bot, 'chat', { + timeout, checkCondition: (username, message) => username === to && listen(message) }) @@ -168,6 +184,7 @@ function inject (bot) { const detectChildJoin = async () => { const [message] = await onceWithCleanup(bot, 'message', { + timeout, checkCondition: message => message.json.translate === 'multiplayer.player.joined' }) childBotName = message.json.with[0].insertion diff --git a/test/externalTests/trade.js b/test/externalTests/trade.js index 7651a0dfc..ad3399069 100644 --- a/test/externalTests/trade.js +++ b/test/externalTests/trade.js @@ -2,6 +2,16 @@ const assert = require('assert') const { once } = require('../../lib/promise_utils') module.exports = () => async (bot) => { + function expectAmount (amount, greaterThan) { + // TODO: 1.20.5+ does not seem to respect "Count" NBT anymore in /summon + // ...as NBT was removed in favor of components that may have something to do + if (bot.registry.version['>=']('1.20.5')) { + if (amount < 1) throw new Error(`${amount} < 1`) // accept anything >=1 + } else { + assert.strictEqual(amount, greaterThan) + } + } + const Item = require('prismarine-item')(bot.registry) const villagerType = bot.registry.entitiesByName.villager ? 'villager' : 'Villager' @@ -31,6 +41,8 @@ module.exports = () => async (bot) => { assert(entity.name === villagerType) const villager = await bot.openVillager(entity) + console.log('Opened villager') + console.dir(villager, { depth: null }) // Handle trade #1 -- takes 2x emerald and returns 2x pumpkin_pie { @@ -40,16 +52,16 @@ module.exports = () => async (bot) => { const [input] = trade.inputs assert.strictEqual(input.name, 'emerald') - assert.strictEqual(input.count, 2) + expectAmount(input.count, 2) const [output] = trade.outputs assert.strictEqual(output.name, 'pumpkin_pie') - assert.strictEqual(output.count, 2) + expectAmount(output.count, 2) await bot.trade(villager, 0, 11) shouldHaveEmeralds -= testFluctuations ? (2 * 2 * 11) : (2 * 11) - assert.strictEqual(bot.currentWindow.count(bot.registry.itemsByName.emerald.id), shouldHaveEmeralds) - assert.strictEqual(bot.currentWindow.count(bot.registry.itemsByName.pumpkin_pie.id), 22) + expectAmount(bot.currentWindow.count(bot.registry.itemsByName.emerald.id), shouldHaveEmeralds) + expectAmount(bot.currentWindow.count(bot.registry.itemsByName.pumpkin_pie.id), 22) } // Handle trade #2 -- takes [2x emerald, 2x pumpkin_pie] and returns 2x wheat @@ -60,19 +72,19 @@ module.exports = () => async (bot) => { const [input1, input2] = trade.inputs assert.strictEqual(input1.name, 'emerald') - assert.strictEqual(input1.count, 2) + expectAmount(input1.count, 2) assert.strictEqual(input2.name, 'pumpkin_pie') - assert.strictEqual(input2.count, 2) + expectAmount(input2.count, 2) const [output] = trade.outputs assert.strictEqual(output.name, 'wheat') - assert.strictEqual(output.count, 2) + expectAmount(output.count, 2) await bot.trade(villager, 1, 11) shouldHaveEmeralds -= 11 * 2 - assert.strictEqual(bot.currentWindow.count(bot.registry.itemsByName.emerald.id), shouldHaveEmeralds) + expectAmount(bot.currentWindow.count(bot.registry.itemsByName.emerald.id), shouldHaveEmeralds) assert.strictEqual(bot.currentWindow.count(bot.registry.itemsByName.pumpkin_pie.id), 0) - assert.strictEqual(bot.currentWindow.count(bot.registry.itemsByName.wheat.id), 22) + expectAmount(bot.currentWindow.count(bot.registry.itemsByName.wheat.id), 22) } // Handle trade #3 -- takes 1x emerald and returns 4x glass @@ -83,16 +95,16 @@ module.exports = () => async (bot) => { const [input] = trade.inputs assert.strictEqual(input.name, 'emerald') - assert.strictEqual(input.count, 1) + expectAmount(input.count, 1) const [output] = trade.outputs assert.strictEqual(output.name, 'glass') - assert.strictEqual(output.count, 4) + expectAmount(output.count, 4) await bot.trade(villager, 2, 11) shouldHaveEmeralds -= 11 - assert.strictEqual(bot.currentWindow.count(bot.registry.itemsByName.emerald.id), shouldHaveEmeralds) - assert.strictEqual(bot.currentWindow.count(bot.registry.itemsByName.glass.id), 44) + expectAmount(bot.currentWindow.count(bot.registry.itemsByName.emerald.id), shouldHaveEmeralds) + expectAmount(bot.currentWindow.count(bot.registry.itemsByName.glass.id), 44) } // Handle trade #4 -- takes [36x emerald, 1x book] and returns 1x wooden sword @@ -103,7 +115,7 @@ module.exports = () => async (bot) => { const [input1, input2] = trade.inputs assert.strictEqual(input1.name, 'emerald') - assert.strictEqual(input1.count, 36) + expectAmount(input1.count, 36) assert.strictEqual(input2.name, 'book') assert.strictEqual(input2.count, 1) @@ -113,9 +125,9 @@ module.exports = () => async (bot) => { await bot.trade(villager, 3, 11) shouldHaveEmeralds -= 11 * 36 - assert.strictEqual(bot.currentWindow.count(bot.registry.itemsByName.emerald.id), shouldHaveEmeralds) + expectAmount(bot.currentWindow.count(bot.registry.itemsByName.emerald.id), shouldHaveEmeralds) assert.strictEqual(bot.currentWindow.count(bot.registry.itemsByName.book.id), 0) - assert.strictEqual(bot.currentWindow.count(bot.registry.itemsByName.wooden_sword.id), 11) + expectAmount(bot.currentWindow.count(bot.registry.itemsByName.wooden_sword.id), 11) } function verifyTrade (trade) { diff --git a/test/externalTests/useChests.js b/test/externalTests/useChests.js index 16526e1cf..66f4ca3bb 100644 --- a/test/externalTests/useChests.js +++ b/test/externalTests/useChests.js @@ -152,9 +152,11 @@ module.exports = () => async (bot) => { if (Math.random() < slotPopulationFactor) { const randomItem = getRandomStackableItem() const item = bot.registry.itemsByName[randomItem] - console.log('createRandomLayout', randomItem, bot.registry.itemsByName) bot.chat(`/give ${bot.username} ${item.name} ${Math.ceil(Math.random() * item.stackSize)}`) - await onceWithCleanup(window, 'updateSlot', { checkCondition: (slot, oldItem, newItem) => slot === window.hotbarStart && newItem?.name === item.name }) + await onceWithCleanup(window, 'updateSlot', { + timeout: 5000, + checkCondition: (slot, oldItem, newItem) => slot === window.hotbarStart && newItem?.name === item.name + }) // await bot.clickWindow(slot, 0, 2) await bot.moveSlotItem(window.hotbarStart, slot) diff --git a/test/internalTest.js b/test/internalTest.js index a8d917bfa..4c930d650 100644 --- a/test/internalTest.js +++ b/test/internalTest.js @@ -58,6 +58,7 @@ for (const supportedVersion of mineflayer.testedVersions) { // 25565 - local server, 25566 - proxy server port: 25567 }) + console.log('Server Codec', server.registryCodec) server.on('listening', () => { bot = mineflayer.createBot({ username: 'player', @@ -440,6 +441,13 @@ for (const supportedVersion of mineflayer.testedVersions) { } } } + if (bot.supportFeature('spawnRespawnWorldDataField')) { + respawnPacket = { + worldState: respawnPacket + } + respawnPacket.worldState.name = loginPacket.worldName + respawnPacket.worldState.dimension = loginPacket.dimension + } } else { respawnPacket = { dimension: 0, @@ -467,8 +475,14 @@ for (const supportedVersion of mineflayer.testedVersions) { assert.ok(bot.world.getColumn(0, 0) === undefined) done() }) - respawnPacket.worldName = 'minecraft:nether' - if (bot.supportFeature('usesLoginPacket')) { + if (bot.supportFeature('spawnRespawnWorldDataField')) { + respawnPacket.worldState.name = 'minecraft:nether' + } else { + respawnPacket.worldName = 'minecraft:nether' + } + if (bot.supportFeature('spawnRespawnWorldDataField')) { + respawnPacket.worldState.dimension = 1 + } else if (bot.supportFeature('usesLoginPacket')) { respawnPacket.dimension.name = 'e' } else { respawnPacket.dimension = 1 @@ -870,6 +884,10 @@ for (const supportedVersion of mineflayer.testedVersions) { if (bot.registry.supportFeature('mcDataHasEntityMetadata')) { metadataPacket.metadata[0].type = 'item_stack' } + metadataPacket.metadata[0].value.addedComponentCount = 0 + metadataPacket.metadata[0].value.removedComponentCount = 0 + metadataPacket.metadata[0].value.components = [] + metadataPacket.metadata[0].value.removeComponents = [] client.write('entity_metadata', metadataPacket) }) From 495eaff8172bb537c241b692ca152e7d1c13ee36 Mon Sep 17 00:00:00 2001 From: rom1504bot Date: Sun, 13 Oct 2024 04:24:02 +0200 Subject: [PATCH 11/33] Release 4.21.0 (#3477) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/history.md | 17 +++++++++++++++++ package.json | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/history.md b/docs/history.md index 7073b291d..309edeaa7 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,3 +1,20 @@ +## 4.21.0 +* [1.20.6 (#3412)](https://github.com/PrismarineJS/mineflayer/commit/44fad41c18be5024564e24e1cdee624d35d4d387) (thanks @extremeheat) +* [Update README.md (#3420)](https://github.com/PrismarineJS/mineflayer/commit/1c2a5c0fa78f74a63fabd7edde85c4a74db32dee) (thanks @SilkePilon) +* [types: add pitchSpeed as an option in typings (#3446)](https://github.com/PrismarineJS/mineflayer/commit/05b48ad0dad4cf64a1c11660bac256d7b4015841) (thanks @GenerelSchwerz) +* [Fixed a bug with not closing the window when changing the subserver (#3424)](https://github.com/PrismarineJS/mineflayer/commit/d00c386cfe51cefc361c0ff4d30b100aee9f114a) (thanks @DenisKvak1) +* [Bump @types/node from 20.14.14 to 22.1.0 (#3431)](https://github.com/PrismarineJS/mineflayer/commit/1d461616b514969fdece38e49bfbec747ab8d76a) (thanks @dependabot[bot]) +* [Fix updateSlot event type (#3425)](https://github.com/PrismarineJS/mineflayer/commit/5d39db26a6ab17baac38b68af8ccd3eeb4af3def) (thanks @DenisKvak1) +* [Player hitbox fixes (#3382)](https://github.com/PrismarineJS/mineflayer/commit/78b4eccb4572a821b11c3124b7a593f3b91f1180) (thanks @AreaDenial) +* [Typo fixes (#3418)](https://github.com/PrismarineJS/mineflayer/commit/ef042a242ca9f5fc5820fe4dc2e1d997ef1db202) (thanks @kgurchiek) +* [Added support for 1.18+ edit book packet #3204 (#3373)](https://github.com/PrismarineJS/mineflayer/commit/eb9982aa04973b0086aac68a2847005d77f01a3d) (thanks @unlimitedcoder2) +* [Fix typos (#3381)](https://github.com/PrismarineJS/mineflayer/commit/d581ea7cee4d5b7df9606f671656bb0be0fdbf91) (thanks @data-miner00) +* [Fix typescript types syntax for setCommandBlock (#3366)](https://github.com/PrismarineJS/mineflayer/commit/315cdfc4b1fc2760e4a8a36feb718626a66d5056) (thanks @undefined) +* [Remove invalid sign check (#3328)](https://github.com/PrismarineJS/mineflayer/commit/ec76468c8ac4c6232bad3c2b66d4160f95f58396) (thanks @zardoy) +* [refactor: simplifying the code of blockAtCursor (#3337)](https://github.com/PrismarineJS/mineflayer/commit/dc70f932ac9aaab6e6cdb15057b409b15c3232dd) (thanks @SnowRunescape) +* [Updated setCommandBlock's 3rd argument (#3356)](https://github.com/PrismarineJS/mineflayer/commit/04ad6db404f0da779004b3ddd0e049bf2c6be0a3) (thanks @FlooferLand) +* [Added the serverBrand property to index.d.ts (#3355)](https://github.com/PrismarineJS/mineflayer/commit/0bb2707d2f6d0d64a467d4e0d6ddc52adf526127) (thanks @Khaogamermain01) + ## 4.20.1 * [Add bossBarCreated event in index.d.ts (#3340)](https://github.com/PrismarineJS/mineflayer/commit/8299288526cd7ff24bcd87511814221f8ad62507) (thanks @gguio) * [Update scoreboard.js (#3318)](https://github.com/PrismarineJS/mineflayer/commit/195b3cbd70a110080af9b77a4659991c5d9e484a) (thanks @vicdum) diff --git a/package.json b/package.json index b805cadb3..6d1888fb1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mineflayer", - "version": "4.20.1", + "version": "4.21.0", "description": "create minecraft bots with a stable, high level API", "main": "index.js", "types": "index.d.ts", From eb29d350ede0590fce17e04bf21071807a87e3a1 Mon Sep 17 00:00:00 2001 From: extremeheat Date: Mon, 14 Oct 2024 18:02:06 -0400 Subject: [PATCH 12/33] Remove debug logging (#3478) --- examples/echo.js | 8 ++++++-- lib/plugins/game.js | 2 -- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/echo.js b/examples/echo.js index b7bd929b8..51f36c6c9 100644 --- a/examples/echo.js +++ b/examples/echo.js @@ -9,7 +9,7 @@ const mineflayer = require('mineflayer') if (process.argv.length < 4 || process.argv.length > 6) { - console.log('Usage : node echo.js [] []') + console.log('Usage : node echo.js [] [online?]') process.exit(1) } @@ -17,10 +17,14 @@ const bot = mineflayer.createBot({ host: process.argv[2], port: parseInt(process.argv[3]), username: process.argv[4] ? process.argv[4] : 'echo', - password: process.argv[5] + auth: process.argv[5] ? 'microsoft' : 'offline' }) bot.on('chat', (username, message) => { if (username === bot.username) return bot.chat(message) }) + +bot.on('kicked', (reason) => { + console.log('I was kicked from the server: ' + reason) +}) diff --git a/lib/plugins/game.js b/lib/plugins/game.js index b9dedef2d..c992d1f74 100644 --- a/lib/plugins/game.js +++ b/lib/plugins/game.js @@ -55,7 +55,6 @@ function inject (bot, options) { if (packet.worldType && !bot.game.dimension) { bot.game.dimension = packet.worldType.replace('minecraft:', '') } - console.log('*Dimension data', bot.game.dimension, bot.registry.dimensionsByName, packet) const dimData = bot.registry.dimensionsByName[bot.game.dimension] if (dimData) { bot.game.minY = dimData.minY @@ -79,7 +78,6 @@ function inject (bot, options) { // 1.20.2 bot._client.on('registry_data', (packet) => { - console.log('Loading Dimension Codec', JSON.stringify(packet).slice(0, 100)) bot.registry.loadDimensionCodec(packet.codec || packet) }) From af3ba9554670e8f5115f876156c0fc80d3bb0503 Mon Sep 17 00:00:00 2001 From: rom1504bot Date: Tue, 15 Oct 2024 00:26:44 +0200 Subject: [PATCH 13/33] Release 4.22.0 (#3479) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/history.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/history.md b/docs/history.md index 309edeaa7..d7eeafa06 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,3 +1,6 @@ +## 4.22.0 +* [Remove debug logging (#3478)](https://github.com/PrismarineJS/mineflayer/commit/eb29d350ede0590fce17e04bf21071807a87e3a1) (thanks @extremeheat) + ## 4.21.0 * [1.20.6 (#3412)](https://github.com/PrismarineJS/mineflayer/commit/44fad41c18be5024564e24e1cdee624d35d4d387) (thanks @extremeheat) * [Update README.md (#3420)](https://github.com/PrismarineJS/mineflayer/commit/1c2a5c0fa78f74a63fabd7edde85c4a74db32dee) (thanks @SilkePilon) diff --git a/package.json b/package.json index 6d1888fb1..eef358235 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mineflayer", - "version": "4.21.0", + "version": "4.22.0", "description": "create minecraft bots with a stable, high level API", "main": "index.js", "types": "index.d.ts", From dd00db42ba20682418d8fbd5629e1033dfb0ff20 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Sat, 26 Oct 2024 19:22:08 +0200 Subject: [PATCH 14/33] Adding mindcraft to mineflayer readme --- docs/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/README.md b/docs/README.md index 2569ccbbf..7accc69c0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -234,6 +234,7 @@ The most updated and useful are : ## Projects Using Mineflayer * [Voyager](https://github.com/MineDojo/Voyager) An Open-Ended Embodied Agent with Large Language Models + * [mindcraft](https://github.com/kolbytn/mindcraft) Lib for using mineflayer with LLMs * [rom1504/rbot](https://github.com/rom1504/rbot) - [YouTube - building a spiral staircase](https://www.youtube.com/watch?v=UM1ZV5200S0) - [YouTube - replicating a building](https://www.youtube.com/watch?v=0cQxg9uDnzA) From 4aa10fb45431940504c7809f078f1f410e7fa7a3 Mon Sep 17 00:00:00 2001 From: Madlykeanu <96554689+Madlykeanu@users.noreply.github.com> Date: Sat, 26 Oct 2024 18:06:52 -0400 Subject: [PATCH 15/33] 1.21 (#3480) * add 1.21 to version.js * update readme * added ci step to get correct minecraft data * uses updated prismarine chunk * uses updated prismarine physics * Used fixed pregistry * Add rotation * Back at base pregistry * Update chat.js * update pregistry and ci.yml for mcdata * 1.21.1 * Properly handle data.type in chat.js * update package.json * fishing rod is stronger; make fish area bigger in fishing test * fix internal tests * remove debug install * go back to upstream nmp --------- Co-authored-by: Romain Beaumont Co-authored-by: extremeheat --- docs/README.md | 2 +- lib/plugins/chat.js | 5 +++-- lib/plugins/inventory.js | 3 ++- lib/version.js | 2 +- package.json | 10 +++++----- test/externalTests/fishing.js | 2 +- test/internalTest.js | 23 +++++++++++++++++------ 7 files changed, 30 insertions(+), 17 deletions(-) diff --git a/docs/README.md b/docs/README.md index 7accc69c0..284fa69d1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,7 +17,7 @@ First time using Node.js? You may want to start with the [tutorial](tutorial.md) ## Features - * Supports Minecraft 1.8 to 1.20.5 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19 and 1.20 upto 1.20.6) + * Supports Minecraft 1.8 to 1.21 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, and 1.21) * Entity knowledge and tracking. * Block knowledge. You can query the world around you. Milliseconds to find any block. * Physics and movement - handle all bounding boxes diff --git a/lib/plugins/chat.js b/lib/plugins/chat.js index 9522223c6..ce92cc9c5 100644 --- a/lib/plugins/chat.js +++ b/lib/plugins/chat.js @@ -118,10 +118,11 @@ function inject (bot, options) { target: data.targetName ? JSON.parse(data.targetName) : undefined, content: message ? JSON.parse(message) : { text: data.plainMessage } } - msg = ChatMessage.fromNetwork(data.type, parameters) + const registryIndex = data.type.registryIndex ? data.type.registryIndex : data.type + msg = ChatMessage.fromNetwork(registryIndex, parameters) if (data.unsignedContent) { - msg.unsigned = ChatMessage.fromNetwork(data.type, { sender: parameters.sender, target: parameters.target, content: JSON.parse(data.unsignedContent) }) + msg.unsigned = ChatMessage.fromNetwork(registryIndex, { sender: parameters.sender, target: parameters.target, content: JSON.parse(data.unsignedContent) }) } } else { msg = ChatMessage.fromNotch(message) diff --git a/lib/plugins/inventory.js b/lib/plugins/inventory.js index 0277f3fef..42eab1d50 100644 --- a/lib/plugins/inventory.js +++ b/lib/plugins/inventory.js @@ -122,7 +122,8 @@ function inject (bot, { hideErrors }) { }) } else if (bot.supportFeature('useItemWithOwnPacket')) { bot._client.write('use_item', { - hand: offHand ? 1 : 0 + hand: offHand ? 1 : 0, + rotation: { x: 0, y: 0 } }) } } diff --git a/lib/version.js b/lib/version.js index 755db7285..291917cb0 100644 --- a/lib/version.js +++ b/lib/version.js @@ -1,4 +1,4 @@ -const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2', '1.20.4', '1.20.6'] +const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2', '1.20.4', '1.20.6', '1.21.1'] module.exports = { testedVersions, diff --git a/package.json b/package.json index eef358235..46d5a27b1 100644 --- a/package.json +++ b/package.json @@ -22,17 +22,17 @@ "license": "MIT", "dependencies": { "minecraft-data": "^3.76.0", - "minecraft-protocol": "^1.49.0", + "minecraft-protocol": "^1.50.0", "prismarine-biome": "^1.1.1", "prismarine-block": "^1.17.0", "prismarine-chat": "^1.7.1", - "prismarine-chunk": "^1.34.0", + "prismarine-chunk": "^1.36.0", "prismarine-entity": "^2.3.0", "prismarine-item": "^1.15.0", "prismarine-nbt": "^2.0.0", - "prismarine-physics": "^1.8.0", + "prismarine-physics": "^1.9.0", "prismarine-recipe": "^1.3.0", - "prismarine-registry": "^1.8.0", + "prismarine-registry": "^1.10.0", "prismarine-windows": "^2.9.0", "prismarine-world": "^3.6.0", "protodef": "1.17.0", @@ -43,7 +43,7 @@ "@types/node": "^22.1.0", "doctoc": "^2.0.1", "minecraft-wrap": "^1.3.0", - "mineflayer": "file:.", + "mineflayer": "file:", "mocha": "^10.0.0", "protodef-yaml": "^1.5.3", "standard": "^17.0.0", diff --git a/test/externalTests/fishing.js b/test/externalTests/fishing.js index c0348c77d..cad7dc427 100644 --- a/test/externalTests/fishing.js +++ b/test/externalTests/fishing.js @@ -1,7 +1,7 @@ module.exports = () => async (bot) => { const Item = require('prismarine-item')(bot.registry) - bot.test.sayEverywhere('/fill ~-5 ~-1 ~-5 ~5 ~-1 ~5 water') + bot.test.sayEverywhere('/fill ~-10 ~-1 ~-10 ~10 ~-1 ~10 water') await bot.test.setInventorySlot(36, new Item(bot.registry.itemsByName.fishing_rod.id, 1, 0)) await bot.lookAt(bot.entity.position) // dont force the position bot.fish() diff --git a/test/internalTest.js b/test/internalTest.js index 4c930d650..2f74e03d1 100644 --- a/test/internalTest.js +++ b/test/internalTest.js @@ -58,7 +58,6 @@ for (const supportedVersion of mineflayer.testedVersions) { // 25565 - local server, 25566 - proxy server port: 25567 }) - console.log('Server Codec', server.registryCodec) server.on('listening', () => { bot = mineflayer.createBot({ username: 'player', @@ -130,7 +129,19 @@ for (const supportedVersion of mineflayer.testedVersions) { const uuid = 'd3527a0b-bc03-45d5-a878-2aafdd8c8a43' // random const networkName = chatText('gary') - if (registry.supportFeature('useChatSessions')) { + if (registry.supportFeature('incrementedChatType')) { + client.write('player_chat', { + plainMessage: 'hello', + filterType: 0, + type: { registryIndex: 1 }, + networkName, + previousMessages: [], + senderUuid: uuid, + timestamp: Date.now(), + index: 0, + salt: 0n + }) + } else if (registry.supportFeature('useChatSessions')) { client.write('player_chat', { plainMessage: 'hello', filterType: 0, @@ -909,9 +920,9 @@ for (const supportedVersion of mineflayer.testedVersions) { const zombieId = entities.zombie ? entities.zombie.id : entities.Zombie.id let bedBlock - if (mineflayer.supportFeature('oneBlockForSeveralVariations', version.majorVersion)) { + if (bot.supportFeature('oneBlockForSeveralVariations', version.majorVersion)) { bedBlock = blocks.bed - } else if (mineflayer.supportFeature('blockSchemeIsFlat', version.majorVersion)) { + } else if (bot.supportFeature('blockSchemeIsFlat', version.majorVersion)) { bedBlock = blocks.red_bed } const bedId = bedBlock.id @@ -945,7 +956,7 @@ for (const supportedVersion of mineflayer.testedVersions) { chunk.setBlockType(beds[bed].foot, bedId) } - if (mineflayer.supportFeature('blockStateId', version.majorVersion)) { + if (bot.supportFeature('blockStateId', version.majorVersion)) { chunk.setBlockStateId(beds[0].foot, 3 + bedBlock.minStateId) // { facing: north, occupied: false, part: foot } chunk.setBlockStateId(beds[0].head, 2 + bedBlock.minStateId) // { facing:north, occupied: false, part: head } @@ -957,7 +968,7 @@ for (const supportedVersion of mineflayer.testedVersions) { chunk.setBlockStateId(beds[3].foot, 11 + bedBlock.minStateId) // { facing: west, occupied: false, part: foot } chunk.setBlockStateId(beds[3].head, 10 + bedBlock.minStateId) // { facing: west, occupied: false, part: head } - } else if (mineflayer.supportFeature('blockMetadata', version.majorVersion)) { + } else if (bot.supportFeature('blockMetadata', version.majorVersion)) { chunk.setBlockData(beds[0].foot, 2) // { facing: north, occupied: false, part: foot } chunk.setBlockData(beds[0].head, 10) // { facing:north, occupied: false, part: head } From 3f1f0a3fef9aef3113d2de62d70e6e42410b0b44 Mon Sep 17 00:00:00 2001 From: rom1504bot Date: Sun, 27 Oct 2024 00:08:33 +0200 Subject: [PATCH 16/33] Release 4.23.0 (#3485) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/history.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/history.md b/docs/history.md index d7eeafa06..1b6a77a27 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,3 +1,7 @@ +## 4.23.0 +* [1.21 (#3480)](https://github.com/PrismarineJS/mineflayer/commit/4aa10fb45431940504c7809f078f1f410e7fa7a3) (thanks @Madlykeanu) +* [Adding mindcraft to mineflayer readme](https://github.com/PrismarineJS/mineflayer/commit/dd00db42ba20682418d8fbd5629e1033dfb0ff20) (thanks @rom1504) + ## 4.22.0 * [Remove debug logging (#3478)](https://github.com/PrismarineJS/mineflayer/commit/eb29d350ede0590fce17e04bf21071807a87e3a1) (thanks @extremeheat) diff --git a/package.json b/package.json index 46d5a27b1..20bda92bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mineflayer", - "version": "4.22.0", + "version": "4.23.0", "description": "create minecraft bots with a stable, high level API", "main": "index.js", "types": "index.d.ts", From 3d6e2344751c38428701dc52e9f29dda73f7f782 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Sun, 27 Oct 2024 00:11:54 +0200 Subject: [PATCH 17/33] increase timeout in external test common --- test/externalTests/plugins/testCommon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/externalTests/plugins/testCommon.js b/test/externalTests/plugins/testCommon.js index fa8bdeaa3..0309fa4bf 100644 --- a/test/externalTests/plugins/testCommon.js +++ b/test/externalTests/plugins/testCommon.js @@ -225,7 +225,7 @@ function inject (bot) { } try { - await withTimeout(Promise.all([detectChildJoin(), runExampleOnReady()]), 20000) + await withTimeout(Promise.all([detectChildJoin(), runExampleOnReady()]), 30000) } catch (err) { console.log(err) return closeExample(err) From fdba03737ecdeaaf419e3175b9be33291db4e085 Mon Sep 17 00:00:00 2001 From: Rocco A <43506958+GenerelSchwerz@users.noreply.github.com> Date: Sat, 16 Nov 2024 02:31:54 -0500 Subject: [PATCH 18/33] Set `sequence` field correctly in activateItem (#3445) * updating activateItem * updating activateItem * fix lint * moved sequence upward * moved sequence upward * removed comment, these tests passing feel like roulette. * reorganized --------- Co-authored-by: extremeheat --- lib/plugins/inventory.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/plugins/inventory.js b/lib/plugins/inventory.js index 42eab1d50..d538711d3 100644 --- a/lib/plugins/inventory.js +++ b/lib/plugins/inventory.js @@ -26,6 +26,7 @@ function inject (bot, { hideErrors }) { const windows = require('prismarine-windows')(bot.version) let eatingTask = createDoneTask() + let sequence = 0 let nextActionNumber = 0 // < 1.17 let stateId = -1 @@ -43,6 +44,7 @@ function inject (bot, { hideErrors }) { bot.inventory = windows.createWindow(0, 'minecraft:inventory', 'Inventory') bot.currentWindow = null bot.usingHeldItem = false + Object.defineProperty(bot, 'heldItem', { get: function () { return bot.inventory.slots[bot.QUICK_BAR_START + bot.quickBarSlot] @@ -111,6 +113,8 @@ function inject (bot, { hideErrors }) { function activateItem (offHand = false) { bot.usingHeldItem = true + sequence++ + if (bot.supportFeature('useItemWithBlockPlace')) { bot._client.write('block_place', { location: new Vec3(-1, 255, -1), @@ -123,17 +127,26 @@ function inject (bot, { hideErrors }) { } else if (bot.supportFeature('useItemWithOwnPacket')) { bot._client.write('use_item', { hand: offHand ? 1 : 0, + sequence, rotation: { x: 0, y: 0 } }) } } function deactivateItem () { - bot._client.write('block_dig', { + const body = { status: 5, location: new Vec3(0, 0, 0), face: 5 - }) + } + + if (bot.supportFeature('useItemWithOwnPacket')) { + body.face = 0 + body.sequence = 0 + } + + bot._client.write('block_dig', body) + bot.usingHeldItem = false } From 3829a25150eec782bc045a222476865af7b0ac72 Mon Sep 17 00:00:00 2001 From: SMED <61847310+SMEDjs@users.noreply.github.com> Date: Sat, 16 Nov 2024 03:49:18 -0500 Subject: [PATCH 19/33] Proper title event (#3498) * titles event * only 1 event * remove chatmsg * add docs and example * LINT --- docs/api.md | 13 +++++++------ docs/br/api_br.md | 9 +++++---- docs/es/api_es.md | 7 ++++--- docs/ru/api_ru.md | 11 ++++++----- docs/zh/api.md | 11 ++++++----- examples/titles.js | 22 ++++++++++++++++++++++ index.d.ts | 2 +- lib/plugins/title.js | 9 +++++++-- 8 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 examples/titles.js diff --git a/docs/api.md b/docs/api.md index e8ae76d1a..e4d8ad9e1 100644 --- a/docs/api.md +++ b/docs/api.md @@ -169,7 +169,7 @@ - ["respawn"](#respawn) - ["game"](#game) - ["resourcePack" (url, hash)](#resourcepack-url-hash) - - ["title"](#title) + - ["title" (title, type)](#title-title-type) - ["rain"](#rain) - ["weatherUpdate"](#weatherupdate) - ["time"](#time) @@ -1237,11 +1237,12 @@ Emitted when the server changes any of the game properties. Emitted when the server sends a resource pack. -#### "title" +#### "title" (title, type) Emitted when the server sends a title - * `text` - title's text + * `title` - title's text + * `type` - title's type "subtitle" or "title" #### "rain" @@ -1771,7 +1772,7 @@ Checks if the given plugin is loaded (or scheduled to be loaded) on this bot. This function returns a `Promise`, with `void` as its argument upon completion. -Sleep in a bed. `bedBlock` should be a `Block` instance which is a bed. +Sleep in a bed. `bedBlock` should be a `Block` instance which is a bed. #### bot.isABed(bedBlock) @@ -1781,7 +1782,7 @@ Return true if `bedBlock` is a bed This function returns a `Promise`, with `void` as its argument upon completion. -Get out of bed. +Get out of bed. #### bot.setControlState(control, state) @@ -2120,7 +2121,7 @@ These are lower level methods for the inventory, they can be useful sometimes bu #### bot.clickWindow(slot, mouseButton, mode) This function returns a `Promise`, with `void` as its argument upon completion. - + The only valid mode option at the moment is 0. Shift clicking or mouse dragging is not implemented. Click on the current window. See details at https://wiki.vg/Protocol#Click_Container diff --git a/docs/br/api_br.md b/docs/br/api_br.md index ec5d4fde1..811386c36 100644 --- a/docs/br/api_br.md +++ b/docs/br/api_br.md @@ -141,7 +141,7 @@ - ["respawn"](#respawn) - ["game"](#game) - ["resourcePack" (url, hash)](#resourcepack-url-hash) - - ["title"](#title) + - ["title" (title, type)](#title-title-type) - ["rain"](#rain) - ["weatherUpdate"](#weatherupdate) - ["time"](#time) @@ -1060,11 +1060,12 @@ Este evento é emitido quando o arquivo index é carregado. Você pode carregar É emitido quando o servidor envia um pacote de recursos. -#### "title" +#### "title" (title, type) É emitido quando o servidor exibe um título. - * `text` - texto do título + * `title` - texto do título + * `type` - tipo do título "subtitle" ou "title" #### "rain" @@ -2035,4 +2036,4 @@ Observação: enquanto você voa, `bot.entity.velocity` não é preciso. #### bot.creative.stopFlying() -Restaura `bot.physics.gravity` ao seu valor original. \ No newline at end of file +Restaura `bot.physics.gravity` ao seu valor original. diff --git a/docs/es/api_es.md b/docs/es/api_es.md index bf72704c1..b26e77efd 100644 --- a/docs/es/api_es.md +++ b/docs/es/api_es.md @@ -141,7 +141,7 @@ - ["respawn"](#respawn) - ["game"](#game) - ["resourcePack" (url, hash)](#resourcepack-url-hash) - - ["title"](#title) + - ["title" (title, type)](#title-title-type) - ["rain"](#rain) - ["weatherUpdate"](#weatherupdate) - ["time"](#time) @@ -1071,11 +1071,12 @@ Se emite cuando el servidor cambia cualquiera de sus propiedades Se emite cuando el servidor manda un paquete de recursos -#### "title" +#### "title" (title, type) Se emite cuando el servidor manda/muestra un título - * `text` - texto del título + * `title` - texto del título + * `type` - tipo del título "subtitle" o "title" #### "rain" diff --git a/docs/ru/api_ru.md b/docs/ru/api_ru.md index 82b42d57a..aa1b69b46 100644 --- a/docs/ru/api_ru.md +++ b/docs/ru/api_ru.md @@ -169,7 +169,7 @@ - ["respawn"](#respawn) - ["game"](#game) - ["resourcePack" (url, hash)](#resourcepack-url-hash) - - ["title"](#title) + - ["title" (title, type)](#title-title-type) - ["rain"](#rain) - ["weatherUpdate"](#weatherupdate) - ["time"](#time) @@ -1264,11 +1264,12 @@ UUID существа, который определяется боссом. Срабатывает, когда сервер отправляет ресурспак. -#### "title" +#### "title" (title, type) Срабатывает, когда сервер отправляет текст по центру экрана. - * `text` - Текст на экране. + * `title` - Текст на экране. + * `type` - Тип текста "subtitle" или "title" #### "rain" @@ -1634,7 +1635,7 @@ UUID существа, который определяется боссом. #### bot.recipesFor(itemType, metadata, minResultCount, craftingTable) -Возвращает список рецептов(`Recipe`), которые вы можете использовать для крафта +Возвращает список рецептов(`Recipe`), которые вы можете использовать для крафта предмета(`itemType`) с мета-данными(`metadata`). * `itemType` - Числовой ID предмета, который вы хотите создать. @@ -2116,7 +2117,7 @@ bot.once('login', () => { #### bot.clickWindow(slot, mouseButton, mode) Эта функция возвращает `Promise` с `void` в качестве аргумента при завершении. - + Единственное действительное значение для `mode` - 0. Нажатие с шифтом или перемещение через мышь не реализовано. Нажимает на текущее окно. Подробнее - https://wiki.vg/Protocol#Click_Container diff --git a/docs/zh/api.md b/docs/zh/api.md index 6a08e62e5..dd5d8521e 100644 --- a/docs/zh/api.md +++ b/docs/zh/api.md @@ -153,7 +153,7 @@ - ["respawn"](#respawn) - ["game"](#game) - ["resourcePack" (url, hash)](#resourcepack-url-hash) - - ["title"](#title) + - ["title" (title, type)](#title-title-type) - ["rain"](#rain) - ["weatherUpdate"](#weatherupdate) - ["time"](#time) @@ -1127,11 +1127,12 @@ Emitted for every server message, including chats. 当服务器发送资源包时触发 -#### "title" +#### "title" (title, type) 当服务器发送标题时触发 - * `text` - 标题文本 + * `title` - 标题文本 + * `type` - 标题类型 "subtitle" 或 "title" #### "rain" @@ -1150,11 +1151,11 @@ If you join a server where it is already raining, this event will fire. 当bot从服务器被踢出时触发 - `reason`是一条解释你被踢的原因的聊天信息. + `reason`是一条解释你被踢的原因的聊天信息. `loggedIn` 如果客户端在成功登录后被踢出则为`true` -如果kick发生在登录阶段则为 `false` +如果kick发生在登录阶段则为 `false` #### "end" (reason) diff --git a/examples/titles.js b/examples/titles.js new file mode 100644 index 000000000..b709e1602 --- /dev/null +++ b/examples/titles.js @@ -0,0 +1,22 @@ +/* + * An example of how to handle title events from the server. + */ +const mineflayer = require('mineflayer') + +if (process.argv.length < 4 || process.argv.length > 6) { + console.log('Usage : node titles.js [] []') + process.exit(1) +} + +const bot = mineflayer.createBot({ + host: process.argv[2], + port: parseInt(process.argv[3]), + username: process.argv[4] ? process.argv[4] : 'titles', + password: process.argv[5] +}) + +// This event is triggered when the server sends a title to the client. +bot.on('title', (text, type) => { + // type is either "title" or "subtitle" + console.log(`Received ${type}: ${text}`) +}) diff --git a/index.d.ts b/index.d.ts index cef39b685..ed6c697ad 100644 --- a/index.d.ts +++ b/index.d.ts @@ -71,7 +71,7 @@ export interface BotEvents { spawn: () => Promise | void respawn: () => Promise | void game: () => Promise | void - title: (text: string) => Promise | void + title: (text: string, type: "subtitle" | "title") => Promise | void rain: () => Promise | void time: () => Promise | void kicked: (reason: string, loggedIn: boolean) => Promise | void diff --git a/lib/plugins/title.js b/lib/plugins/title.js index f8714d757..44307e270 100644 --- a/lib/plugins/title.js +++ b/lib/plugins/title.js @@ -1,9 +1,14 @@ module.exports = inject - function inject (bot) { bot._client.on('title', (packet) => { if (packet.action === 0 || packet.action === 1) { - bot.emit('title', packet.text) + bot.emit('title', packet.text, 'title') } }) + bot._client.on('set_title_text', packet => { + bot.emit('title', packet.text, 'title') + }) + bot._client.on('set_title_subtitle', packet => { + bot.emit('title', packet.text, 'subtitle') + }) } From 386200759556aa261fa212f26c43992a66cfa8ac Mon Sep 17 00:00:00 2001 From: ShiftSad Date: Wed, 20 Nov 2024 00:02:26 -0300 Subject: [PATCH 20/33] Fix chatterbox example (#3506) --- examples/python/chatterbox.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/python/chatterbox.py b/examples/python/chatterbox.py index 094251300..c3907e6a2 100644 --- a/examples/python/chatterbox.py +++ b/examples/python/chatterbox.py @@ -30,8 +30,7 @@ bot = mineflayer.createBot({ "host": host, "port": port, - "username": username, - "port": port + "username": username }) Item = require("prismarine-item")(bot.registry) @@ -300,4 +299,4 @@ def entityEffect(this, entity, effect): @On(bot, "entityEffectEnd") def entityEffectEnd(this, entity, effect): - print("entityEffectEnd", entity, effect) \ No newline at end of file + print("entityEffectEnd", entity, effect) From 166971d317db3ec68cf3eebeda37f509152628fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 21:24:16 +0100 Subject: [PATCH 21/33] Bump mocha from 10.8.2 to 11.0.1 (#3516) Bumps [mocha](https://github.com/mochajs/mocha) from 10.8.2 to 11.0.1. - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v10.8.2...v11.0.1) --- updated-dependencies: - dependency-name: mocha dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20bda92bb..1d242063a 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "doctoc": "^2.0.1", "minecraft-wrap": "^1.3.0", "mineflayer": "file:", - "mocha": "^10.0.0", + "mocha": "^11.0.1", "protodef-yaml": "^1.5.3", "standard": "^17.0.0", "standard-markdown": "^7.1.0", From 06faa36c2da3da399bd5370869700aea6c65c9b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 23:48:22 +0100 Subject: [PATCH 22/33] Bump protodef from 1.17.0 to 1.18.0 (#3523) * Bump protodef from 1.17.0 to 1.18.0 Bumps [protodef](https://github.com/ProtoDef-io/node-protodef) from 1.17.0 to 1.18.0. - [Release notes](https://github.com/ProtoDef-io/node-protodef/releases) - [Changelog](https://github.com/ProtoDef-io/node-protodef/blob/master/doc/history.md) - [Commits](https://github.com/ProtoDef-io/node-protodef/compare/1.17.0...1.18.0) --- updated-dependencies: - dependency-name: protodef dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Update package.json --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Romain Beaumont --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1d242063a..aee23aa12 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "prismarine-registry": "^1.10.0", "prismarine-windows": "^2.9.0", "prismarine-world": "^3.6.0", - "protodef": "1.17.0", + "protodef": "^1.18.0", "typed-emitter": "^1.0.0", "vec3": "^0.1.7" }, From f2dd3a37505b374bf63119633659e35ec2ce3542 Mon Sep 17 00:00:00 2001 From: Vandeputte Brice Date: Mon, 23 Dec 2024 23:48:44 +0100 Subject: [PATCH 23/33] Update FAQ.md - add mineflayer tested versions : lib/version.js (#3517) * Update FAQ.md - add mineflyer tested versions : lib/version.js Add mineflyer tested versions - lib/version.js - as suggestion on connect issue. This prevent user question on patch exact version to use. * Update FAQ.md - typo --- docs/FAQ.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/FAQ.md b/docs/FAQ.md index 0117fcbad..1629ce96d 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -2,6 +2,10 @@ This Frequently Asked Question document is meant to help people for the most common things. +### I get an error (ie. protocol/data) when bot is trying to connect to minecraft server + +Make sure the Minecraft server version is supported (cf. root readme), else you should retry using one of the [mineflayer tested versions](../lib/version.js). + ### I get an error when trying to login with a microsoft account. Make sure the email you entered into the username option in createBot can be used to login to `minecraft.net` using the 'Login with Microsoft' button. From a0e92cad5887181bf7e235f69378c8ede14a350c Mon Sep 17 00:00:00 2001 From: Pix3lPirat3 Date: Mon, 23 Dec 2024 14:49:26 -0800 Subject: [PATCH 24/33] Update inventory.js (#3507) --- lib/plugins/inventory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/inventory.js b/lib/plugins/inventory.js index d538711d3..d38048b7b 100644 --- a/lib/plugins/inventory.js +++ b/lib/plugins/inventory.js @@ -263,7 +263,7 @@ function inject (bot, { hideErrors }) { const itemType = options.itemType const metadata = options.metadata const nbt = options.nbt - let count = options.count === null ? 1 : options.count + let count = (options.count === undefined || options.count === null) ? 1 : options.count let firstSourceSlot = null // ranges From 71a3a262681a173db86b8911aec82402a6993d21 Mon Sep 17 00:00:00 2001 From: Madlykeanu <96554689+Madlykeanu@users.noreply.github.com> Date: Mon, 23 Dec 2024 15:50:36 -0700 Subject: [PATCH 25/33] update contribution docs to show test running commands (#3511) --- docs/CONTRIBUTING.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 4ca6412bc..205619c38 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -22,6 +22,22 @@ Mineflayer has two kind of tests : The objective of these tests is to know automatically what works and what doesn't in mineflayer, so it's easier to make mineflayer work. + +## Running tests +You can run tests for Different Minecraft versions using the `-g` flag with npm run mocha_test. For example: + +```bash +# Run all tests in all supported versions +npm run test + +# Run a specific test in Minecraft 1.20.4 +npm run mocha_test -- -g "mineflayer_external 1.20.4v.*exampleBee" + +# Run all tests in just version 1.20.4 +npm run mocha_test -- -g "mineflayer_external 1.20.4v" +``` + + ### Creating an external test In order to add an external test now you only need to create a file in [test/externalTests](test/externalTests) From f6187f66c16dd122165287be7864c78b2fe7c32c Mon Sep 17 00:00:00 2001 From: Vitaly Date: Tue, 24 Dec 2024 01:51:58 +0300 Subject: [PATCH 26/33] fix: use dimension data on 1.16.2 (#3397) * fix: use dimension data on 1.16.2 * retrigger ci --- lib/plugins/blocks.js | 2 +- lib/plugins/game.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/plugins/blocks.js b/lib/plugins/blocks.js index 6a9c1655a..a2f41ad6f 100644 --- a/lib/plugins/blocks.js +++ b/lib/plugins/blocks.js @@ -261,7 +261,7 @@ function inject (bot, { version, storageBuilder, hideErrors }) { bot.world.setColumn(packet.chunkX, packet.chunkZ, column) } - if (bot.supportFeature('dimensionDataIsAvailable')) { + if (bot.supportFeature('newLightingDataFormat')) { column.loadParsedLight(packet.skyLight, packet.blockLight, packet.skyLightMask, packet.blockLightMask, packet.emptySkyLightMask, packet.emptyBlockLightMask) } else { column.loadLight(packet.data, packet.skyLightMask, packet.blockLightMask, packet.emptySkyLightMask, packet.emptyBlockLightMask) diff --git a/lib/plugins/game.js b/lib/plugins/game.js index c992d1f74..98649297a 100644 --- a/lib/plugins/game.js +++ b/lib/plugins/game.js @@ -60,7 +60,7 @@ function inject (bot, options) { bot.game.minY = dimData.minY bot.game.height = dimData.height } - } else if (bot.supportFeature('dimensionDataIsAvailable')) { // 1.18 + } else if (bot.supportFeature('dimensionDataIsAvailable')) { // 1.16.2+ const dimensionData = nbt.simplify(packet.dimension) bot.game.minY = dimensionData.min_y bot.game.height = dimensionData.height From 3187368397e880ba8b32bb03affa18203cbcbb42 Mon Sep 17 00:00:00 2001 From: wAIfu-DEV <132222556+wAIfu-DEV@users.noreply.github.com> Date: Wed, 1 Jan 2025 18:01:57 +0100 Subject: [PATCH 27/33] Fix out of bounds access leading to crash at spawn (#3535) More information about the issue at #3534 --- lib/plugins/breath.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/breath.js b/lib/plugins/breath.js index 7ac316824..137140366 100644 --- a/lib/plugins/breath.js +++ b/lib/plugins/breath.js @@ -11,7 +11,7 @@ function inject (bot) { if (bot.entity.id !== packet.entityId) return for (const metadata of packet.metadata) { if (metadata.key === 1) { - bot.oxygenLevel = Math.round(packet.metadata[1].value / 15) + bot.oxygenLevel = Math.round(metadata.value / 15) bot.emit('breath') } } From 58ae9e5b5abf75139f4ba93fe4f34ef7ed3936e8 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 8 Jan 2025 09:51:37 +0100 Subject: [PATCH 28/33] Support 1.21.3. (#3489) * Support 1.21.3. * Update version.js * Update entities.js * Update package.json * Update ci.yml * Fix gamemode test and implementation (#3508) * Fix gamemode test and implementation * Add gamemode test related comments * Fix gamemode tests Add test function to kill the bot * Add gamemode out of bounds checks * Simplify gameMode parsing and check against spawnRespawnWorldDataField feature * 21.3 work * fix * reduce debug logging * outbound logging * fix logging * disable logging and --bail * Update internalTest.js typo * Fix 1.21.3 set creative slot * revert some setInventorySlot checks * fix internalTest and check * use features * Update physics.js fix lastSent * Remove debug logging --------- Co-authored-by: extremeheat Co-authored-by: IceTank <61137113+IceTank@users.noreply.github.com> --- lib/plugins/blocks.js | 11 ++- lib/plugins/creative.js | 24 ++++- lib/plugins/entities.js | 113 +++++++++++++++++++++-- lib/plugins/game.js | 14 ++- lib/plugins/generic_place.js | 4 +- lib/plugins/inventory.js | 18 ++-- lib/plugins/physics.js | 89 +++++++++++++----- lib/plugins/settings.js | 3 +- lib/version.js | 2 +- package.json | 4 +- test/externalTests/gamemode.js | 29 +++++- test/externalTests/plugins/testCommon.js | 22 +++++ test/internalTest.js | 53 +++++++++-- 13 files changed, 319 insertions(+), 67 deletions(-) diff --git a/lib/plugins/blocks.js b/lib/plugins/blocks.js index a2f41ad6f..8a719c93b 100644 --- a/lib/plugins/blocks.js +++ b/lib/plugins/blocks.js @@ -395,10 +395,13 @@ function inject (bot, { version, storageBuilder, hideErrors }) { bot._client.on('explosion', (packet) => { // explosion const p = new Vec3(packet.x, packet.y, packet.z) - packet.affectedBlockOffsets.forEach((offset) => { - const pt = p.offset(offset.x, offset.y, offset.z) - updateBlockState(pt, 0) - }) + if (packet.affectedBlockOffsets) { + // TODO: server no longer sends in 1.21.3. Is client supposed to compute this or is it sent via normal block updates? + packet.affectedBlockOffsets.forEach((offset) => { + const pt = p.offset(offset.x, offset.y, offset.z) + updateBlockState(pt, 0) + }) + } }) bot._client.on('spawn_entity_painting', (packet) => { diff --git a/lib/plugins/creative.js b/lib/plugins/creative.js index 573f581ee..cd66a72c8 100644 --- a/lib/plugins/creative.js +++ b/lib/plugins/creative.js @@ -21,7 +21,7 @@ function inject (bot) { const creativeSlotsUpdates = [] // WARN: This method should not be called twice on the same slot before first promise succeeds - async function setInventorySlot (slot, item) { + async function setInventorySlot (slot, item, waitTimeout = 400) { assert(slot >= 0 && slot <= 44) if (Item.equal(bot.inventory.slots[slot], item, true)) return @@ -29,12 +29,32 @@ function inject (bot) { throw new Error(`Setting slot ${slot} cancelled due to calling bot.creative.setInventorySlot(${slot}, ...) again`) } creativeSlotsUpdates[slot] = true - bot._client.write('set_creative_slot', { slot, item: Item.toNotch(item) }) + if (bot.supportFeature('noAckOnCreateSetSlotPacket')) { + // No ack + bot._setSlot(slot, item) + if (waitTimeout === 0) return // no wait + // allow some time to see if server rejects + return new Promise((resolve, reject) => { + function updateSlot (oldItem, newItem) { + if (newItem.itemId !== item.itemId) { + creativeSlotsUpdates[slot] = false + reject(Error('Server rejected')) + } + } + bot.inventory.once(`updateSlot:${slot}`, updateSlot) + setTimeout(() => { + bot.inventory.off(`updateSlot:${slot}`, updateSlot) + creativeSlotsUpdates[slot] = false + resolve() + }, waitTimeout) + }) + } + await onceWithCleanup(bot.inventory, `updateSlot:${slot}`, { timeout: 5000, checkCondition: (oldItem, newItem) => item === null ? newItem === null : newItem?.name === item.name && newItem?.count === item.count && newItem?.metadata === item.metadata diff --git a/lib/plugins/entities.js b/lib/plugins/entities.js index 49a5b8696..4fbf8c6d8 100644 --- a/lib/plugins/entities.js +++ b/lib/plugins/entities.js @@ -341,6 +341,16 @@ function inject (bot) { bot.emit('entityMoved', entity) }) + // 1.21.3 - merges the packets above + bot._client.on('sync_entity_position', (packet) => { + const entity = fetchEntity(packet.entityId) + entity.position.set(packet.x, packet.y, packet.z) + entity.velocity.update(packet.dx, packet.dy, packet.dz) + entity.yaw = packet.yaw + entity.pitch = packet.pitch + bot.emit('entityMoved', entity) + }) + bot._client.on('entity_head_rotation', (packet) => { // entity head look const entity = fetchEntity(packet.entityId) @@ -361,6 +371,11 @@ function inject (bot) { if (eventName) bot.emit(eventName, entity) }) + bot._client.on('damage_event', (packet) => { // 1.20+ + const entity = bot.entities[packet.entityId] + bot.emit('entityHurt', entity) + }) + bot._client.on('attach_entity', (packet) => { // attach entity const entity = fetchEntity(packet.entityId) @@ -585,6 +600,62 @@ function inject (bot) { bot._client.on('player_info', (packet) => { // player list item(s) + if (typeof packet.action !== 'number') { + // the features checks below this will be un-needed with https://github.com/PrismarineJS/minecraft-data/pull/948 + for (const update of packet.data) { + let player = bot.uuidToUsername[update.uuid] ? bot.players[bot.uuidToUsername[update.uuid]] : null + let newPlayer = false + + const obj = { + uuid: update.uuid + } + + if (!player) newPlayer = true + + player ||= obj + + if (packet.action.add_player) { + obj.username = update.player.name + obj.displayName = player.displayName || new ChatMessage({ text: '', extra: [{ text: update.player.name }] }) + obj.skinData = extractSkinInformation(update.player.properties) + } + + if (packet.action.update_game_mode) { + obj.gamemode = update.gamemode + } + + if (packet.action.update_latency) { + obj.ping = update.latency + } + + if (update.displayName) { + obj.displayName = ChatMessage.fromNotch(update.displayName) + } + + if (newPlayer) { + if (!obj.username) continue // Should be unreachable + player = bot.players[obj.username] = obj + bot.uuidToUsername[obj.uuid] = obj.username + } else { + Object.assign(player, obj) + } + + const playerEntity = Object.values(bot.entities).find(e => e.type === 'player' && e.username === player.username) + player.entity = playerEntity + + if (playerEntity === bot.entity) { + bot.player = player + } + + if (newPlayer) { + bot.emit('playerJoined', player) + } else { + bot.emit('playerUpdated', player) + } + } + return + } + if (bot.supportFeature('playerInfoActionIsBitfield')) { for (const item of packet.data) { let player = bot.uuidToUsername[item.uuid] ? bot.players[bot.uuidToUsername[item.uuid]] : null @@ -795,20 +866,42 @@ function inject (bot) { } function moveVehicle (left, forward) { - bot._client.write('steer_vehicle', { - sideways: left, - forward, - jump: 0x01 - }) + if (bot.supportFeature('newPlayerInputPacket')) { + // docs: + // * left can take -1 or 1 : -1 means right, 1 means left + // * forward can take -1 or 1 : -1 means backward, 1 means forward + bot._client.write('player_input', { + inputs: { + forward: forward > 0, + backward: forward < 0, + left: left > 0, + right: left < 0 + } + }) + } else { + bot._client.write('steer_vehicle', { + sideways: left, + forward, + jump: 0x01 + }) + } } function dismount () { if (bot.vehicle) { - bot._client.write('steer_vehicle', { - sideways: 0.0, - forward: 0.0, - jump: 0x02 - }) + if (bot.supportFeature('newPlayerInputPacket')) { + bot._client.write('player_input', { + inputs: { + jump: true + } + }) + } else { + bot._client.write('steer_vehicle', { + sideways: 0.0, + forward: 0.0, + jump: 0x02 + }) + } } else { bot.emit('error', new Error('dismount: not mounted')) } diff --git a/lib/plugins/game.js b/lib/plugins/game.js index 98649297a..3bbf5b287 100644 --- a/lib/plugins/game.js +++ b/lib/plugins/game.js @@ -10,7 +10,12 @@ const dimensionNames = { 1: 'the_end' } -const parseGameMode = gameModeBits => gameModes[(gameModeBits & 0b11)] // lower two bits +const parseGameMode = gameModeBits => { + if (gameModeBits < 0 || gameModeBits > 0b11) { + return 'survival' + } + return gameModes[(gameModeBits & 0b11)] // lower two bits +} function inject (bot, options) { function getBrandCustomChannelName () { @@ -25,7 +30,12 @@ function inject (bot, options) { function handleRespawnPacketData (packet) { bot.game.levelType = packet.levelType ?? (packet.isFlat ? 'flat' : 'default') bot.game.hardcore = packet.isHardcore ?? Boolean(packet.gameMode & 0b100) - bot.game.gameMode = packet.gamemode || parseGameMode(packet.gameMode) + // Either a respawn packet or a login packet. Depending on the packet it can be "gamemode" or "gameMode" + if (bot.supportFeature('spawnRespawnWorldDataField')) { // 1.20.5 + bot.game.gameMode = packet.gamemode + } else { + bot.game.gameMode = parseGameMode(packet.gamemode ?? packet.gameMode) + } if (bot.supportFeature('segmentedRegistryCodecData')) { // 1.20.5 if (typeof packet.dimension === 'number') { bot.game.dimension = bot.registry.dimensionsArray[packet.dimension]?.name?.replace('minecraft:', '') diff --git a/lib/plugins/generic_place.js b/lib/plugins/generic_place.js index 8fddebacd..448dfc00d 100644 --- a/lib/plugins/generic_place.js +++ b/lib/plugins/generic_place.js @@ -79,7 +79,9 @@ function inject (bot) { cursorX: dx, cursorY: dy, cursorZ: dz, - insideBlock: false + insideBlock: false, + sequence: 0, // 1.19.0 + worldBorderHit: false // 1.21.3 }) } diff --git a/lib/plugins/inventory.js b/lib/plugins/inventory.js index d38048b7b..70db3128f 100644 --- a/lib/plugins/inventory.js +++ b/lib/plugins/inventory.js @@ -190,6 +190,7 @@ function inject (bot, { hideErrors }) { // TODO: tell the server that we are not sneaking while doing this await bot.lookAt(block.position.offset(0.5, 0.5, 0.5), false) // place block message + // TODO: logic below can likely be simplified if (bot.supportFeature('blockPlaceHasHeldItem')) { bot._client.write('block_place', { location: block.position, @@ -225,7 +226,9 @@ function inject (bot, { hideErrors }) { cursorX: cursorPos.x, cursorY: cursorPos.y, cursorZ: cursorPos.z, - insideBlock: false + insideBlock: false, + sequence: 0, // 1.19.0+ + worldBorderHit: false // 1.21.3+ }) } @@ -712,15 +715,18 @@ function inject (bot, { hideErrors }) { bot.currentWindow = null bot.emit('windowClose', oldWindow) }) - bot._client.on('set_slot', (packet) => { + bot._setSlot = (slotId, newItem, window = bot.inventory) => { // set slot + const oldItem = window.slots[slotId] + window.updateSlot(slotId, newItem) + updateHeldItem() + bot.emit(`setSlot:${window.id}`, oldItem, newItem) + } + bot._client.on('set_slot', (packet) => { const window = packet.windowId === 0 ? bot.inventory : bot.currentWindow if (!window || window.id !== packet.windowId) return const newItem = Item.fromNotch(packet.item) - const oldItem = window.slots[packet.slot] - window.updateSlot(packet.slot, newItem) - updateHeldItem() - bot.emit(`setSlot:${window.id}`, oldItem, newItem) + bot._setSlot(packet.slot, newItem, window) }) bot._client.on('window_items', (packet) => { const window = packet.windowId === 0 ? bot.inventory : bot.currentWindow diff --git a/lib/plugins/physics.js b/lib/plugins/physics.js index 578e5a529..588dfbb41 100644 --- a/lib/plugins/physics.js +++ b/lib/plugins/physics.js @@ -48,7 +48,8 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) { yaw: 0, pitch: 0, onGround: false, - time: 0 + time: 0, + flags: { onGround: false, hasHorizontalCollision: false } } // This function should be executed each tick (every 0.05 seconds) @@ -103,6 +104,7 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) { lastSent.y = position.y lastSent.z = position.z lastSent.onGround = onGround + lastSent.flags = { onGround, hasHorizontalCollision: undefined } // 1.21.3+ bot._client.write('position', lastSent) bot.emit('move', oldPos) } @@ -113,6 +115,7 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) { lastSent.yaw = yaw lastSent.pitch = pitch lastSent.onGround = onGround + lastSent.flags = { onGround, hasHorizontalCollision: undefined } // 1.21.3+ bot._client.write('look', lastSent) bot.emit('move', oldPos) } @@ -126,6 +129,7 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) { lastSent.yaw = yaw lastSent.pitch = pitch lastSent.onGround = onGround + lastSent.flags = { onGround, hasHorizontalCollision: undefined } // 1.21.3+ bot._client.write('position_look', lastSent) bot.emit('move', oldPos) } @@ -167,9 +171,9 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) { // Only send a position update if necessary, select the appropriate packet const positionUpdated = lastSent.x !== position.x || lastSent.y !== position.y || lastSent.z !== position.z || - // Send a position update every second, even if no other update was made - // This function rounds to the nearest 50ms (or PHYSICS_INTERVAL_MS) and checks if a second has passed. - (Math.round((now - lastSent.time) / PHYSICS_INTERVAL_MS) * PHYSICS_INTERVAL_MS) >= 1000 + // Send a position update every second, even if no other update was made + // This function rounds to the nearest 50ms (or PHYSICS_INTERVAL_MS) and checks if a second has passed. + (Math.round((now - lastSent.time) / PHYSICS_INTERVAL_MS) * PHYSICS_INTERVAL_MS) >= 1000 const lookUpdated = lastSent.yaw !== yaw || lastSent.pitch !== pitch if (positionUpdated && lookUpdated) { @@ -184,7 +188,10 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) { // For versions < 1.12, one player packet should be sent every tick // for the server to update health correctly // For versions >= 1.12, onGround !== lastSent.onGround should be used, but it doesn't ever trigger outside of login - bot._client.write('flying', { onGround: bot.entity.onGround }) + bot._client.write('flying', { + onGround: bot.entity.onGround, + flags: { onGround: bot.entity.onGround, hasHorizontalCollision: undefined } // 1.21.3+ + }) } lastSent.onGround = bot.entity.onGround // onGround is always set @@ -288,9 +295,14 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) { bot._client.on('explosion', explosion => { // TODO: emit an explosion event with more info if (bot.physicsEnabled && bot.game.gameMode !== 'creative') { - bot.entity.velocity.x += explosion.playerMotionX - bot.entity.velocity.y += explosion.playerMotionY - bot.entity.velocity.z += explosion.playerMotionZ + if (explosion.playerKnockback) { // 1.21.3+ + bot.entity.velocity.add(explosion.playerMotionX, explosion.playerMotionY, explosion.playerMotionZ) + } + if ('playerMotionX' in explosion) { + bot.entity.velocity.x += explosion.playerMotionX + bot.entity.velocity.y += explosion.playerMotionY + bot.entity.velocity.z += explosion.playerMotionZ + } } }) @@ -330,30 +342,59 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) { await bot.look(yaw, pitch, force) } + // 1.21.3+ + bot._client.on('player_rotation', (packet) => { + bot.entity.yaw = conv.fromNotchianYaw(packet.yaw) + bot.entity.pitch = conv.fromNotchianPitch(packet.pitch) + }) + // player position and look (clientbound) bot._client.on('position', (packet) => { // Is this necessary? Feels like it might wrongly overwrite hitbox size sometimes // e.g. when crouching/crawling/swimming. Can someone confirm? bot.entity.height = 1.8 - // Velocity is only set to 0 if the flag is not set, otherwise keep current velocity + // Velocity is reset if the x, y, z flags are not set const vel = bot.entity.velocity - vel.set( - packet.flags & 1 ? vel.x : 0, - packet.flags & 2 ? vel.y : 0, - packet.flags & 4 ? vel.z : 0 - ) - - // If flag is set, then the corresponding value is relative, else it is absolute + // If the x, y, z flags are not set, the position is absolute const pos = bot.entity.position - pos.set( - packet.flags & 1 ? (pos.x + packet.x) : packet.x, - packet.flags & 2 ? (pos.y + packet.y) : packet.y, - packet.flags & 4 ? (pos.z + packet.z) : packet.z - ) - - const newYaw = (packet.flags & 8 ? conv.toNotchianYaw(bot.entity.yaw) : 0) + packet.yaw - const newPitch = (packet.flags & 16 ? conv.toNotchianPitch(bot.entity.pitch) : 0) + packet.pitch + + // TODO: this current mineflayer logic maybe incorrect. New (maybe even older) versions of minecraft have flag values for + // dx/dy/dz but mineflayer is assuming that 0b111 applies for both velocity and position. + + let newYaw, newPitch + + if (bot.registry.version['>=']('1.21.3')) { + // flags is now an object with keys + // "flags": ["x", "y", "z", "yaw", "pitch", "dx", "dy", "dz", "yawDelta"] + const flags = packet.flags + vel.set( + flags.x ? vel.x : 0, + flags.y ? vel.y : 0, + flags.z ? vel.z : 0 + ) + pos.set( + flags.x ? (pos.x + packet.x) : packet.x, + flags.y ? (pos.y + packet.y) : packet.y, + flags.z ? (pos.z + packet.z) : packet.z + ) + newYaw = (flags.yaw ? conv.toNotchianYaw(bot.entity.yaw) : 0) + packet.yaw + newPitch = (flags.pitch ? conv.toNotchianPitch(bot.entity.pitch) : 0) + packet.pitch + } else { + vel.set( + packet.flags & 1 ? vel.x : 0, + packet.flags & 2 ? vel.y : 0, + packet.flags & 4 ? vel.z : 0 + ) + pos.set( + packet.flags & 1 ? (pos.x + packet.x) : packet.x, + packet.flags & 2 ? (pos.y + packet.y) : packet.y, + packet.flags & 4 ? (pos.z + packet.z) : packet.z + ) + newYaw = (packet.flags & 8 ? conv.toNotchianYaw(bot.entity.yaw) : 0) + packet.yaw + newPitch = (packet.flags & 16 ? conv.toNotchianPitch(bot.entity.pitch) : 0) + packet.pitch + } + bot.entity.yaw = conv.fromNotchianYaw(newYaw) bot.entity.pitch = conv.fromNotchianPitch(newPitch) bot.entity.onGround = false diff --git a/lib/plugins/settings.js b/lib/plugins/settings.js index a835e3851..a3bdd7cd9 100644 --- a/lib/plugins/settings.js +++ b/lib/plugins/settings.js @@ -87,7 +87,8 @@ function inject (bot, options) { : options.skinParts, mainHand: options.mainHand || 'right', enableTextFiltering: options.enableTextFiltering || false, - enableServerListing: options.enableServerListing || true + enableServerListing: options.enableServerListing || true, + particleStatus: 'all' } bot._client.on('login', () => { diff --git a/lib/version.js b/lib/version.js index 291917cb0..8623a4c59 100644 --- a/lib/version.js +++ b/lib/version.js @@ -1,4 +1,4 @@ -const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2', '1.20.4', '1.20.6', '1.21.1'] +const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2', '1.20.4', '1.20.6', '1.21.1', '1.21.3'] module.exports = { testedVersions, diff --git a/package.json b/package.json index aee23aa12..0d0dbf5e3 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "types": "index.d.ts", "scripts": { - "mocha_test": "mocha --reporter spec --exit --bail", + "mocha_test": "mocha --reporter spec --exit", "test": "npm run mocha_test", "pretest": "npm run lint", "lint": "standard && standard-markdown", @@ -22,7 +22,7 @@ "license": "MIT", "dependencies": { "minecraft-data": "^3.76.0", - "minecraft-protocol": "^1.50.0", + "minecraft-protocol": "^1.51.0", "prismarine-biome": "^1.1.1", "prismarine-block": "^1.17.0", "prismarine-chat": "^1.7.1", diff --git a/test/externalTests/gamemode.js b/test/externalTests/gamemode.js index d4cc45a04..ebf2d1487 100644 --- a/test/externalTests/gamemode.js +++ b/test/externalTests/gamemode.js @@ -1,10 +1,29 @@ // test to see if bot retains creative gamemode in bot object on death const assert = require('assert') +const { onceWithCleanup } = require('../../lib/promise_utils') -module.exports = async (bot) => { - bot.test.becomeCreative() - bot.chat(`/kill ${bot.username}`) - await new Promise((resolve, reject) => setTimeout(resolve, 5000)) - assert.strictEqual(bot.game.gameMode, 'creative', 'Failed to parse respawn packet') +module.exports = () => { + const tests = [] + + function addTest (name, f) { + tests[name] = (bot) => f(bot) + } + + addTest('change', async (bot) => { + await bot.test.becomeSurvival() + assert.strictEqual(bot.game.gameMode, 'survival', 'Wrong gamemode after switching gamemode') + await bot.test.becomeCreative() + assert.strictEqual(bot.game.gameMode, 'creative', 'Wrong gamemode after switching gamemode') + }) + + addTest('after respawn', async (bot) => { + await bot.test.becomeCreative() + bot.test.selfKill() + await onceWithCleanup(bot, 'respawn', { timeout: 5000 }) + // Respawn packets send the gamemode. If the bot is in creative mode, it should respawn in creative mode. Tested <1.20 + assert.strictEqual(bot.game.gameMode, 'creative', 'Wrong gamemode after respawn') + }) + + return tests } diff --git a/test/externalTests/plugins/testCommon.js b/test/externalTests/plugins/testCommon.js index 0309fa4bf..9249e605c 100644 --- a/test/externalTests/plugins/testCommon.js +++ b/test/externalTests/plugins/testCommon.js @@ -25,6 +25,7 @@ function inject (bot) { bot.test.placeBlock = placeBlock bot.test.runExample = runExample bot.test.tellAndListen = tellAndListen + bot.test.selfKill = selfKill bot.test.wait = function (ms) { return new Promise((resolve) => { setTimeout(resolve, ms) }) } @@ -232,4 +233,25 @@ function inject (bot) { } return closeExample() } + + function selfKill () { + bot.chat('/kill @p') + } + + // Debug packet IO when tests are re-run with "Enable debug logging" - https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables + if (process.env.RUNNER_DEBUG) { + bot._client.on('packet', function (data, meta) { + if (['chunk', 'time', 'light', 'alive'].some(e => meta.name.includes(e))) return + console.log('->', meta.name, JSON.stringify(data)?.slice(0, 250)) + }) + const oldWrite = bot._client.write + bot._client.write = function (name, data) { + if (['alive', 'pong', 'ping'].some(e => name.includes(e))) return + console.log('<-', name, JSON.stringify(data)?.slice(0, 250)) + oldWrite.apply(bot._client, arguments) + } + BigInt.prototype.toJSON ??= function () { // eslint-disable-line + return this.toString() + } + } } diff --git a/test/internalTest.js b/test/internalTest.js index 2f74e03d1..cad701aa6 100644 --- a/test/internalTest.js +++ b/test/internalTest.js @@ -11,8 +11,24 @@ for (const supportedVersion of mineflayer.testedVersions) { const registry = require('prismarine-registry')(supportedVersion) const version = registry.version const Chunk = require('prismarine-chunk')(supportedVersion) - const hasSignedChat = registry.supportFeature('signedChat') + const isNewPlayerInfoFormat = registry.version['>=']('1.21.3') + function wrapPlayerInfo (n) { + if (isNewPlayerInfoFormat) { + return { + _value: n, + add_player: (n & 1) !== 0, + initialize_chat: (n & 2) !== 0, + update_game_mode: (n & 4) !== 0, + update_listed: (n & 8) !== 0, + update_latency: (n & 16) !== 0, + update_display_name: (n & 32) !== 0, + update_priority: (n & 64) !== 0 + } + } + return n + } + const hasSignedChat = registry.supportFeature('signedChat') function chatText (text) { // TODO: move this to prismarine-chat in a new ChatMessage(text).toNotch(asNbt) method return registry.supportFeature('chatPacketsUseNbtComponents') @@ -254,9 +270,12 @@ for (const supportedVersion of mineflayer.testedVersions) { x: 1.5, y: 66, z: 1.5, + dx: 0, // 1.21.3 + dy: 0, // 1.21.3 + dz: 0, // 1.21.3 pitch: 0, yaw: 0, - flags: 0, + flags: bot.registry.version['>=']('1.21.3') ? {} : 0, teleportId: 0 } server.on('playerJoin', async (client) => { @@ -351,9 +370,22 @@ for (const supportedVersion of mineflayer.testedVersions) { x: 1, y: 0, z: 0, + dx: 0, // 1.21.3 + dy: 0, // 1.21.3 + dz: 0, // 1.21.3 pitch: 0, yaw: 0, - flags: 31, + flags: bot.registry.version['>=']('1.21.3') + ? { + // flags = ["x", "y", "z", "yaw", "pitch", "dx", "dy", "dz", "yawDelta"] + // 31 = 0b11111 + x: true, + y: true, + z: true, + yaw: true, + pitch: true + } + : 31, teleportId: 3 } absolute = false @@ -561,10 +593,12 @@ for (const supportedVersion of mineflayer.testedVersions) { server.on('playerJoin', (client) => { bot.on('entitySpawn', (entity) => { const player = bot.players[entity.username] - assert.strictEqual(entity.username, player.displayName.toString()) + assert.strictEqual(player.username, entity.username) + // TODO: this test is broken as it updates the display name twice (once with, once without) + if (!isNewPlayerInfoFormat) assert.strictEqual(entity.username, player.displayName.toString()) if (registry.supportFeature('playerInfoActionIsBitfield')) { client.write('player_info', { - action: 53, + action: wrapPlayerInfo(53), data: [{ uuid: '1-2-3-4', player: { @@ -600,7 +634,7 @@ for (const supportedVersion of mineflayer.testedVersions) { assert.strictEqual('wvffle', player.displayName.toString()) if (registry.supportFeature('playerInfoActionIsBitfield')) { client.write('player_info', { - action: 53, + action: wrapPlayerInfo(53), data: [{ uuid: '1-2-3-4', player: { @@ -630,14 +664,15 @@ for (const supportedVersion of mineflayer.testedVersions) { } bot.once('playerUpdated', (player) => { - assert.strictEqual(player.entity.username, player.displayName.toString()) + // TODO: this test is broken as it updates the display name twice (once with, once without) + if (!isNewPlayerInfoFormat) assert.strictEqual(player.entity.username, player.displayName.toString()) done() }) }) if (registry.supportFeature('playerInfoActionIsBitfield')) { client.write('player_info', { - action: 53, + action: wrapPlayerInfo(53), data: [{ uuid: '1-2-3-4', player: { @@ -720,7 +755,7 @@ for (const supportedVersion of mineflayer.testedVersions) { if (registry.supportFeature('playerInfoActionIsBitfield')) { client.write('player_info', { - action: 53, + action: wrapPlayerInfo(53), data: [{ uuid: '1-2-3-4', player: { From 793220bdfb906986db33dc15e7844d613e0cb966 Mon Sep 17 00:00:00 2001 From: rom1504bot Date: Wed, 8 Jan 2025 10:17:20 +0100 Subject: [PATCH 29/33] Release 4.24.0 (#3545) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/history.md | 14 ++++++++++++++ package.json | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/history.md b/docs/history.md index 1b6a77a27..89eebc288 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,3 +1,17 @@ +## 4.24.0 +* [Support 1.21.3. (#3489)](https://github.com/PrismarineJS/mineflayer/commit/58ae9e5b5abf75139f4ba93fe4f34ef7ed3936e8) (thanks @rom1504) +* [Fix out of bounds access leading to crash at spawn (#3535)](https://github.com/PrismarineJS/mineflayer/commit/3187368397e880ba8b32bb03affa18203cbcbb42) (thanks @wAIfu-DEV) +* [fix: use dimension data on 1.16.2 (#3397)](https://github.com/PrismarineJS/mineflayer/commit/f6187f66c16dd122165287be7864c78b2fe7c32c) (thanks @zardoy) +* [update contribution docs to show test running commands (#3511)](https://github.com/PrismarineJS/mineflayer/commit/71a3a262681a173db86b8911aec82402a6993d21) (thanks @Madlykeanu) +* [Update inventory.js (#3507)](https://github.com/PrismarineJS/mineflayer/commit/a0e92cad5887181bf7e235f69378c8ede14a350c) (thanks @Pix3lPirat3) +* [Update FAQ.md - add mineflayer tested versions : lib/version.js (#3517)](https://github.com/PrismarineJS/mineflayer/commit/f2dd3a37505b374bf63119633659e35ec2ce3542) (thanks @boly38) +* [Bump protodef from 1.17.0 to 1.18.0 (#3523)](https://github.com/PrismarineJS/mineflayer/commit/06faa36c2da3da399bd5370869700aea6c65c9b0) (thanks @dependabot[bot]) +* [Bump mocha from 10.8.2 to 11.0.1 (#3516)](https://github.com/PrismarineJS/mineflayer/commit/166971d317db3ec68cf3eebeda37f509152628fd) (thanks @dependabot[bot]) +* [Fix chatterbox example (#3506)](https://github.com/PrismarineJS/mineflayer/commit/386200759556aa261fa212f26c43992a66cfa8ac) (thanks @ShiftSad) +* [Proper title event (#3498)](https://github.com/PrismarineJS/mineflayer/commit/3829a25150eec782bc045a222476865af7b0ac72) (thanks @SMEDjs) +* [Set `sequence` field correctly in activateItem (#3445)](https://github.com/PrismarineJS/mineflayer/commit/fdba03737ecdeaaf419e3175b9be33291db4e085) (thanks @GenerelSchwerz) +* [increase timeout in external test common](https://github.com/PrismarineJS/mineflayer/commit/3d6e2344751c38428701dc52e9f29dda73f7f782) (thanks @rom1504) + ## 4.23.0 * [1.21 (#3480)](https://github.com/PrismarineJS/mineflayer/commit/4aa10fb45431940504c7809f078f1f410e7fa7a3) (thanks @Madlykeanu) * [Adding mindcraft to mineflayer readme](https://github.com/PrismarineJS/mineflayer/commit/dd00db42ba20682418d8fbd5629e1033dfb0ff20) (thanks @rom1504) diff --git a/package.json b/package.json index 0d0dbf5e3..d71b20db8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mineflayer", - "version": "4.23.0", + "version": "4.24.0", "description": "create minecraft bots with a stable, high level API", "main": "index.js", "types": "index.d.ts", From 8bbf5669f5ff3ea8a708633e51b47c312dc7a26b Mon Sep 17 00:00:00 2001 From: extremeheat Date: Wed, 8 Jan 2025 16:11:51 -0500 Subject: [PATCH 30/33] 1.21.4 (#3546) * 1.21.4 * debug * undo logging * Update ci.yml * Update ci.yml * Update ci.yml --------- Co-authored-by: Romain Beaumont --- lib/version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/version.js b/lib/version.js index 8623a4c59..3b8e171a1 100644 --- a/lib/version.js +++ b/lib/version.js @@ -1,4 +1,4 @@ -const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2', '1.20.4', '1.20.6', '1.21.1', '1.21.3'] +const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2', '1.20.4', '1.20.6', '1.21.1', '1.21.3', '1.21.4'] module.exports = { testedVersions, From b8f5814d9d8c2cab5c317fb7190b1daf7c7cac21 Mon Sep 17 00:00:00 2001 From: rom1504bot Date: Wed, 8 Jan 2025 22:13:50 +0100 Subject: [PATCH 31/33] Release 4.25.0 (#3547) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/history.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/history.md b/docs/history.md index 89eebc288..7d6a5009b 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,3 +1,6 @@ +## 4.25.0 +* [1.21.4 (#3546)](https://github.com/PrismarineJS/mineflayer/commit/8bbf5669f5ff3ea8a708633e51b47c312dc7a26b) (thanks @extremeheat) + ## 4.24.0 * [Support 1.21.3. (#3489)](https://github.com/PrismarineJS/mineflayer/commit/58ae9e5b5abf75139f4ba93fe4f34ef7ed3936e8) (thanks @rom1504) * [Fix out of bounds access leading to crash at spawn (#3535)](https://github.com/PrismarineJS/mineflayer/commit/3187368397e880ba8b32bb03affa18203cbcbb42) (thanks @wAIfu-DEV) diff --git a/package.json b/package.json index d71b20db8..97d59ee57 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mineflayer", - "version": "4.24.0", + "version": "4.25.0", "description": "create minecraft bots with a stable, high level API", "main": "index.js", "types": "index.d.ts", From 08b7317b57ca5c2f1a6ddd116bd0c74c2e10c20c Mon Sep 17 00:00:00 2001 From: Lukas <77849373+qwqtoday@users.noreply.github.com> Date: Wed, 22 Jan 2025 02:25:18 +0800 Subject: [PATCH 32/33] Mounting for other entities and fix bot not dismounting when the vehicle is gone (#3384) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * dismounting when the vehicle is gone * solve my problems temporary * add mounting and dismounting for other entities * Revert unnecessary changes This reverts commit a95ee68e063a2420812c6ca45d7ec3f494953442. * not checking the entityId when applying passenger vehicle and vehicle passengers in `attach_entity` in `entities.js` * make sure the vehicle passengers are correctly removed * rename vehicle to originalVehicle * remove passenger from vehicle.passengers after passenger left that vehicle * push passenger to vehicle.passengers * remove that 💀 * ensure vehicle is not null in `set_passengers` * Fix linting errors * Empty commit * Point the prismarine-entity package to my branch * Update package.json --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: extremeheat Co-authored-by: Romain Beaumont --- lib/plugins/entities.js | 77 +++++++++++++++++++++++++++++++++-------- package.json | 2 +- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/lib/plugins/entities.js b/lib/plugins/entities.js index 4fbf8c6d8..86dd361e7 100644 --- a/lib/plugins/entities.js +++ b/lib/plugins/entities.js @@ -803,26 +803,73 @@ function inject (bot) { // attaching to a vehicle bot._client.on('attach_entity', (packet) => { - if (packet.entityId !== bot.entity.id) return - const vehicle = bot.vehicle - if (packet.vehicleId === -1) { - bot.vehicle = null - bot.emit('dismount', vehicle) - } else { - bot.vehicle = bot.entities[packet.vehicleId] - bot.emit('mount') + const passenger = fetchEntity(packet.entityId) + const vehicle = packet.vehicleId === -1 ? null : fetchEntity(packet.vehicleId) + + const originalVehicle = passenger.vehicle + if (originalVehicle !== null) { + const index = originalVehicle.passengers.indexOf(passenger) + originalVehicle.passengers = originalVehicle.passengers.splice(index, 1) + } + passenger.vehicle = vehicle + vehicle.passengers.push(passenger) + + if (packet.entityId === bot.entity.id) { + const vehicle = bot.vehicle + if (packet.vehicleId === -1) { + bot.vehicle = null + bot.emit('dismount', vehicle) + } else { + bot.vehicle = bot.entities[packet.vehicleId] + bot.emit('mount') + } } }) bot._client.on('set_passengers', ({ entityId, passengers }) => { - if (passengers[0] !== bot.entity.id) return - const vehicle = bot.vehicle - if (entityId === -1) { + const passengerEntities = passengers.map((passengerId) => fetchEntity(passengerId)) + const vehicle = entityId === -1 ? null : bot.entities[entityId] + + for (const passengerEntity of passengerEntities) { + const originalVehicle = passengerEntity.vehicle + if (originalVehicle !== null) { + const index = originalVehicle.passengers.indexOf(passengerEntity) + originalVehicle.passengers = originalVehicle.passengers.splice(index, 1) + } + passengerEntity.vehicle = vehicle + if (vehicle !== null) { + vehicle.passengers.push(passengerEntity) + } + } + + if (passengers.includes(bot.entity.id)) { + const originalVehicle = bot.vehicle + if (entityId === -1) { + bot.vehicle = null + bot.emit('dismount', originalVehicle) + } else { + bot.vehicle = bot.entities[entityId] + bot.emit('mount') + } + } + }) + + // dismounting when the vehicle is gone + bot._client.on('entityGone', (entity) => { + if (bot.vehicle === entity) { bot.vehicle = null - bot.emit('dismount', vehicle) - } else { - bot.vehicle = bot.entities[entityId] - bot.emit('mount') + bot.emit('dismount', (entity)) + } + if (entity.passengers) { + for (const passenger of entity.passengers) { + passenger.vehicle = null + } + } + if (entity.vehicle) { + const index = entity.vehicle.passengers.indexOf(entity) + if (index !== -1) { + entity.vehicle.passengers = entity.vehicle.passengers.splice(index, 1) + } } }) diff --git a/package.json b/package.json index 97d59ee57..897d8193e 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "prismarine-block": "^1.17.0", "prismarine-chat": "^1.7.1", "prismarine-chunk": "^1.36.0", - "prismarine-entity": "^2.3.0", + "prismarine-entity": "^2.5.0", "prismarine-item": "^1.15.0", "prismarine-nbt": "^2.0.0", "prismarine-physics": "^1.9.0", From e974e703a875d16f5e36d35574bf334a20c76b1b Mon Sep 17 00:00:00 2001 From: artren Date: Tue, 21 Jan 2025 21:33:32 +0300 Subject: [PATCH 33/33] Prevent TypeError when removing event lPrevent TypeError when removing event listeners in switchWorld (BunJS issue maybe)isteners in switchWorld (#3544) Added a check to ensure that only valid function listeners are removed using bot.off --- lib/plugins/blocks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/blocks.js b/lib/plugins/blocks.js index 8a719c93b..f128f71a9 100644 --- a/lib/plugins/blocks.js +++ b/lib/plugins/blocks.js @@ -511,7 +511,7 @@ function inject (bot, { version, storageBuilder, hideErrors }) { } for (const [name, listener] of Object.entries(bot._events)) { - if (name.startsWith('blockUpdate:')) { + if (name.startsWith('blockUpdate:') && typeof listener === 'function') { bot.emit(name, null, null) bot.off(name, listener) }