From 6501e83d44fa2ac17fe94fa4d5a18ec67ea6d608 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Tue, 12 Aug 2025 14:35:23 -0500 Subject: [PATCH 1/4] first pass at getting doors to work --- patches/mineflayer-pathfinder+2.4.5.patch | 182 +++++++++++++++++++++- src/agent/agent.js | 1 + src/agent/library/skills.js | 42 ++++- 3 files changed, 219 insertions(+), 6 deletions(-) diff --git a/patches/mineflayer-pathfinder+2.4.5.patch b/patches/mineflayer-pathfinder+2.4.5.patch index 6906371..bfc4ad0 100644 --- a/patches/mineflayer-pathfinder+2.4.5.patch +++ b/patches/mineflayer-pathfinder+2.4.5.patch @@ -1,8 +1,35 @@ diff --git a/node_modules/mineflayer-pathfinder/index.js b/node_modules/mineflayer-pathfinder/index.js -index b38bd30..bf16a63 100644 +index b38bd30..fb39b45 100644 --- a/node_modules/mineflayer-pathfinder/index.js +++ b/node_modules/mineflayer-pathfinder/index.js -@@ -550,6 +550,7 @@ function inject (bot) { +@@ -170,6 +170,16 @@ function inject (bot) { + const curPoint = path[i] + if (curPoint.toBreak.length > 0 || curPoint.toPlace.length > 0) break + const b = bot.blockAt(new Vec3(curPoint.x, curPoint.y, curPoint.z)) ++ ++ // openned doors have small Collision box ++ // that may stop the bot from moving forward ++ if(i === 0 && b.name.includes('door')) { ++ curPoint.x = Math.floor(curPoint.x) + 0.5 ++ curPoint.y = Math.floor(curPoint.y) ++ curPoint.z = Math.floor(curPoint.z) + 0.5 ++ continue ++ } ++ + if (b && (b.type === waterType || ((b.type === ladderId || b.type === vineId) && i + 1 < path.length && path[i + 1].y < curPoint.y))) { + curPoint.x = Math.floor(curPoint.x) + 0.5 + curPoint.y = Math.floor(curPoint.y) +@@ -524,6 +534,9 @@ function inject (bot) { + bot.activateBlock(bot.blockAt(new Vec3(placingBlock.x, placingBlock.y, placingBlock.z))).then(() => { + lockUseBlock.release() + placingBlock = nextPoint.toPlace.shift() ++ if (!placingBlock) { ++ placing = false ++ } + }, err => { + console.error(err) + lockUseBlock.release() +@@ -550,6 +563,7 @@ function inject (bot) { lockEquipItem.release() const refBlock = bot.blockAt(new Vec3(placingBlock.x, placingBlock.y, placingBlock.z), false) if (!lockPlaceBlock.tryAcquire()) return @@ -10,7 +37,7 @@ index b38bd30..bf16a63 100644 if (interactableBlocks.includes(refBlock.name)) { bot.setControlState('sneak', true) } -@@ -557,6 +558,7 @@ function inject (bot) { +@@ -557,6 +571,7 @@ function inject (bot) { .then(function () { // Dont release Sneak if the block placement was not successful bot.setControlState('sneak', false) @@ -18,3 +45,152 @@ index b38bd30..bf16a63 100644 if (bot.pathfinder.LOSWhenPlacingBlocks && placingBlock.returnPos) returningPos = placingBlock.returnPos.clone() }) .catch(_ignoreError => { +diff --git a/node_modules/mineflayer-pathfinder/lib/movements.js b/node_modules/mineflayer-pathfinder/lib/movements.js +index a7e3505..3c4a8f2 100644 +--- a/node_modules/mineflayer-pathfinder/lib/movements.js ++++ b/node_modules/mineflayer-pathfinder/lib/movements.js +@@ -62,7 +62,13 @@ + + this.climbables = new Set() + this.climbables.add(registry.blocksByName.ladder.id) +- // this.climbables.add(registry.blocksByName.vine.id) ++ if (registry.blocksByName.vine) this.climbables.add(registry.blocksByName.vine.id) ++ if (registry.blocksByName.weeping_vines) this.climbables.add(registry.blocksByName.weeping_vines.id) ++ if (registry.blocksByName.weeping_vines_plant) this.climbables.add(registry.blocksByName.weeping_vines_plant.id) ++ if (registry.blocksByName.twisting_vines) this.climbables.add(registry.blocksByName.twisting_vines.id) ++ if (registry.blocksByName.twisting_vines_plant) this.climbables.add(registry.blocksByName.twisting_vines_plant.id) ++ if (registry.blocksByName.cave_vines) this.climbables.add(registry.blocksByName.cave_vines.id) ++ if (registry.blocksByName.cave_vines_plant) this.climbables.add(registry.blocksByName.cave_vines_plant.id) + this.emptyBlocks = new Set() + + this.replaceables = new Set() +@@ -92,13 +98,15 @@ + } + }) + registry.blocksArray.forEach(block => { +- if (this.interactableBlocks.has(block.name) && block.name.toLowerCase().includes('gate') && !block.name.toLowerCase().includes('iron')) { ++ if (this.interactableBlocks.has(block.name) ++ && (block.name.toLowerCase().includes('gate') || block.name.toLowerCase().includes('door') || block.name.toLowerCase().includes('trapdoor')) ++ && !block.name.toLowerCase().includes('iron')) { + // console.info(block) + this.openable.add(block.id) + } + }) + +- this.canOpenDoors = false // Causes issues. Probably due to none paper servers. ++ this.canOpenDoors = true + + this.exclusionAreasStep = [] + this.exclusionAreasBreak = [] +@@ -230,8 +238,13 @@ + } + } + b.climbable = this.climbables.has(b.type) +- b.safe = (b.boundingBox === 'empty' || b.climbable || this.carpets.has(b.type)) && !this.blocksToAvoid.has(b.type) +- b.physical = b.boundingBox === 'block' && !this.fences.has(b.type) ++ ++ // Enhanced trapdoor logic - open trapdoors are safe to pass through ++ const isOpenTrapdoor = this.openable.has(b.type) && b.name.includes('trapdoor') && b._properties?.open === true ++ const isClosedTrapdoor = this.openable.has(b.type) && b.name.includes('trapdoor') && b._properties?.open !== true ++ ++ b.safe = (b.boundingBox === 'empty' || b.climbable || this.carpets.has(b.type) || isOpenTrapdoor) && !this.blocksToAvoid.has(b.type) ++ b.physical = (b.boundingBox === 'block' && !this.fences.has(b.type)) || isClosedTrapdoor + b.replaceable = this.replaceables.has(b.type) && !b.physical + b.liquid = this.liquids.has(b.type) + b.height = pos.y + dy +@@ -284,6 +297,18 @@ + cost += this.exclusionStep(block) // Is excluded so can't move or break + cost += this.getNumEntitiesAt(block.position, 0, 0, 0) * this.entityCost + if (block.safe) return cost ++ ++ // process door cost ++ if ((this.canOpenDoors && block.openable) ++ || (block.openable && block._properties?.open === true)) { ++ return cost ++ } ++ ++ // Handle trapdoors specifically - they can be opened instead of broken ++ if (this.canOpenDoors && block.openable && block.name.includes('trapdoor') && !block.name.includes('iron')) { ++ return cost + 1 // Small cost for opening trapdoor ++ } ++ + if (!this.safeToBreak(block)) return 100 // Can't break, so can't move + toBreak.push(block.position) + +@@ -387,8 +412,8 @@ + cost += this.safeOrBreak(blockB, toBreak) + if (cost > 100) return + +- // Open fence gates +- if (this.canOpenDoors && blockC.openable && blockC.shapes && blockC.shapes.length !== 0) { ++ // Open fence gates and doors ++ if (this.canOpenDoors && blockC.openable && !blockC._properties.open) { + toPlace.push({ x: node.x + dir.x, y: node.y, z: node.z + dir.z, dx: 0, dy: 0, dz: 0, useOne: true }) // Indicate that a block should be used on this block not placed + } else { + cost += this.safeOrBreak(blockC, toBreak) +@@ -552,6 +577,54 @@ + if (cost > 100) return + + neighbors.push(new Move(node.x, node.y + 1, node.z, node.remainingBlocks - toPlace.length, cost, toBreak, toPlace)) ++ } ++ ++ getMoveClimbUpThroughTrapdoor (node, neighbors) { ++ const blockCurrent = this.getBlock(node, 0, 0, 0) // Current position (should be climbable) ++ const blockAbove = this.getBlock(node, 0, 1, 0) // Block directly above ++ const blockCeiling = this.getBlock(node, 0, 2, 0) // Trapdoor or ceiling block ++ ++ // Only attempt this move if we're on a climbable block (ladder/vine) ++ if (!blockCurrent.climbable) return ++ ++ // Check if there's a closed trapdoor above us ++ if (!blockCeiling.openable || blockCeiling._properties?.open === true) return ++ ++ let cost = 2 // Base cost for climbing up and opening trapdoor ++ const toBreak = [] ++ const toPlace = [] ++ ++ // Make sure we can break/pass through the block above if needed ++ cost += this.safeOrBreak(blockAbove, toBreak) ++ if (cost > 100) return ++ ++ // Add cost for opening the trapdoor ++ toPlace.push({ x: node.x, y: node.y + 2, z: node.z, dx: 0, dy: 0, dz: 0, useOne: true }) ++ ++ neighbors.push(new Move(node.x, node.y + 2, node.z, node.remainingBlocks - toPlace.length, cost, toBreak, toPlace)) ++ } ++ ++ // Enhanced ladder/vine climbing that can handle stepping on top and jumping ++ getMoveClimbTop (node, neighbors) { ++ const blockCurrent = this.getBlock(node, 0, 0, 0) // Current position (should be climbable) ++ const blockAbove = this.getBlock(node, 0, 1, 0) // Block directly above ++ ++ // Only attempt this move if we're on a climbable block (ladder/vine) ++ if (!blockCurrent.climbable) return ++ ++ // Check if we can step on top of the ladder/vine and then jump up ++ if (!blockAbove.safe) return ++ ++ let cost = 2 // Cost for climbing to top of ladder and jumping ++ const toBreak = [] ++ const toPlace = [] ++ ++ // Check if there's space to jump up from the top of the ladder ++ const blockJumpTarget = this.getBlock(node, 0, 2, 0) ++ cost += this.safeOrBreak(blockJumpTarget, toBreak) ++ if (cost > 100) return ++ ++ neighbors.push(new Move(node.x, node.y + 2, node.z, node.remainingBlocks - toPlace.length, cost, toBreak, toPlace)) + } + + // Jump up, down or forward over a 1 block gap +@@ -655,6 +728,10 @@ + + this.getMoveDown(node, neighbors) + this.getMoveUp(node, neighbors) ++ ++ // Enhanced climbing moves for ladders, vines, and trapdoors ++ this.getMoveClimbUpThroughTrapdoor(node, neighbors) ++ this.getMoveClimbTop(node, neighbors) + + return neighbors + } \ No newline at end of file diff --git a/src/agent/agent.js b/src/agent/agent.js index d989f24..f478764 100644 --- a/src/agent/agent.js +++ b/src/agent/agent.js @@ -118,6 +118,7 @@ export class Agent { ]; const respondFunc = async (username, message) => { + if (message === "") return; if (username === this.name) return; if (settings.only_chat_with.length > 0 && !settings.only_chat_with.includes(username)) return; try { diff --git a/src/agent/library/skills.js b/src/agent/library/skills.js index e488771..7cdec5f 100644 --- a/src/agent/library/skills.js +++ b/src/agent/library/skills.js @@ -1127,9 +1127,45 @@ export async function goToPlayer(bot, username, distance=3) { return false; } - const move = new pf.Movements(bot); - bot.pathfinder.setMovements(move); - await bot.pathfinder.goto(new pf.goals.GoalFollow(player, distance), true); + + const dontBreakBlocks = ['glass', 'glass_pane']; + const nonDestructiveMovements = new pf.Movements(bot); + nonDestructiveMovements.canOpenDoors = true; + nonDestructiveMovements.allowFreeMotion = true; + nonDestructiveMovements.digCost = 100; + nonDestructiveMovements.blocksCantBreak.add(dontBreakBlocks.map(block => mc.getBlockId(block))); + + const destructiveMovements = new pf.Movements(bot); + destructiveMovements.canOpenDoors = true; + destructiveMovements.allowFreeMotion = true; + + let movements = nonDestructiveMovements; + const goal = new pf.goals.GoalFollow(player, distance); + + const timeout = 5000; + try { + console.log('finding non-destructive path...'); + const nonDestructivePath = await bot.pathfinder.getPathTo(nonDestructiveMovements, goal, timeout); + if ( + nonDestructivePath && + nonDestructivePath.path && + nonDestructivePath.path.length > 0 && + nonDestructivePath.status !== 'noPath' + ) { + console.log('found non-destructive path'); + movements = nonDestructiveMovements; + } + else { + console.log('no non-destructive path found, using destructive path'); + movements = destructiveMovements; + } + } catch (err) { + log(bot, `Could not find a path: ${err.message}.`); + return false; + } + + bot.pathfinder.setMovements(movements); + await bot.pathfinder.goto(goal, true); log(bot, `You have reached ${username}.`); } From ec5d783170f0676f53d3578b847292fb529792f8 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Tue, 12 Aug 2025 17:38:06 -0500 Subject: [PATCH 2/4] temp fix to digging enchantment issue --- patches/prismarine-item+1.17.0.patch | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 patches/prismarine-item+1.17.0.patch diff --git a/patches/prismarine-item+1.17.0.patch b/patches/prismarine-item+1.17.0.patch new file mode 100644 index 0000000..dd8c226 --- /dev/null +++ b/patches/prismarine-item+1.17.0.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/prismarine-item/index.js b/node_modules/prismarine-item/index.js +index a1397a2..1d75cad 100644 +--- a/node_modules/prismarine-item/index.js ++++ b/node_modules/prismarine-item/index.js +@@ -231,7 +231,7 @@ function loader (registryOrVersion) { + const typeOfEnchantLevelValue = registry.supportFeature('typeOfValueForEnchantLevel') + const useStoredEnchantments = registry.supportFeature('booksUseStoredEnchantments') && this.name === 'enchanted_book' + +- if (typeOfEnchantLevelValue === 'short' && enchantNbtKey === 'ench') { ++ if (typeOfEnchantLevelValue === 'short' && (enchantNbtKey === 'ench' || enchantNbtKey === 'componentEnchantments')) { + let itemEnch = [] + + if (useStoredEnchantments && this?.nbt?.value?.StoredEnchantments) { From 0d8719f8ef5a141cca6173958253859fc4223a4a Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Wed, 13 Aug 2025 17:28:21 -0500 Subject: [PATCH 3/4] pretty good door navigation system --- src/agent/library/skills.js | 200 ++++++++++++++++++++++++++---------- src/agent/modes.js | 14 ++- 2 files changed, 158 insertions(+), 56 deletions(-) diff --git a/src/agent/library/skills.js b/src/agent/library/skills.js index 08c460d..bbe8acf 100644 --- a/src/agent/library/skills.js +++ b/src/agent/library/skills.js @@ -4,8 +4,8 @@ import pf from 'mineflayer-pathfinder'; import Vec3 from 'vec3'; import settings from "../../../settings.js"; -const blockPlaceDelay = settings.block_place_delay || 10 -const useDelay = blockPlaceDelay > 0 +const blockPlaceDelay = settings.block_place_delay || 10; +const useDelay = blockPlaceDelay > 0; export function log(bot, message) { bot.output += message + '\n'; @@ -1009,6 +1009,125 @@ export async function giveToPlayer(bot, itemType, username, num=1) { return false; } +export async function goToGoal(bot, goal) { + /** + * Navigate to the given goal, attempting minimally destructive movements. + * @param {MinecraftBot} bot, reference to the minecraft bot. + * @param {pf.goals.Goal} goal, the goal to navigate to. + **/ + + const nonDestructiveMovements = new pf.Movements(bot); + const dontBreakBlocks = ['glass', 'glass_pane']; + for (let block of dontBreakBlocks) { + nonDestructiveMovements.blocksCantBreak.add(mc.getBlockId(block)); + } + nonDestructiveMovements.digCost = 10; + + const destructiveMovements = new pf.Movements(bot); + + let final_movements = null; + let movements = [ + {name: 'nonDestructive', movements: nonDestructiveMovements}, + {name: 'destructive', movements: destructiveMovements} + ]; + + const pathfind_timeout = 1000; + try { + for (let i = 0; i < movements.length; i++) { + const movement = movements[i].movements; + const path = await bot.pathfinder.getPathTo(movement, goal, pathfind_timeout); + if (path && path.path && path.path.length > 0 && path.status !== 'noPath') { + final_movements = movement; + log(bot, `Using ${movements[i].name} movements.`); + break; + } + } + if (!final_movements) { + log(bot, `Could not find a path to ${username}.`); + return false; + } + } catch (err) { + log(bot, `Could not find a path: ${err.message}.`); + return false; + } + + const doorCheckInterval = startDoorInterval(bot); + + bot.pathfinder.setMovements(final_movements); + try { + await bot.pathfinder.goto(goal); + clearInterval(doorCheckInterval); + return true; + } catch (err) { + log(bot, `Could not find a path: ${err.message}.`); + clearInterval(doorCheckInterval); + return false; + } +} + +let _doorInterval = null; +function startDoorInterval(bot) { + /** + * Start helper interval that opens nearby doors if the bot is stuck. + * @param {MinecraftBot} bot, reference to the minecraft bot. + * @returns {number} the interval id. + **/ + if (_doorInterval) { + clearInterval(_doorInterval); + } + let prev_pos = bot.entity.position.clone(); + let prev_check = Date.now(); + let stuck_time = 0; + + // adjacent positions to check for doors + const positions = [ + bot.entity.position.clone(), + bot.entity.position.offset(0, 0, 1), + bot.entity.position.offset(0, 0, -1), + bot.entity.position.offset(1, 0, 0), + bot.entity.position.offset(-1, 0, 0), + ] + let elevated_positions = positions.map(position => position.offset(0, 1, 0)); + positions.push(...elevated_positions); + + const doorCheckInterval = setInterval(() => { + const now = Date.now(); + if (bot.entity.position.distanceTo(prev_pos) >= 0.1) { + stuck_time = 0; + } else { + stuck_time += now - prev_check; + } + + if (stuck_time > 1200) { + // shuffle positions so we're not always opening the same door + let currentIndex = positions.length; + while (currentIndex != 0) { + let randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex--; + [positions[currentIndex], positions[randomIndex]] = [ + positions[randomIndex], positions[currentIndex]]; + } + + for (let position of positions) { + let block = bot.blockAt(position); + if (block && block.name && + !block.name.includes('iron') && + (block.name.includes('door') || + block.name.includes('fence_gate') || + block.name.includes('trapdoor'))) + { + bot.activateBlock(block); + break; + } + } + stuck_time = 0; + } + prev_pos = bot.entity.position.clone(); + prev_check = now; + }, 200); + _doorInterval = doorCheckInterval; + return doorCheckInterval; +} export async function goToPosition(bot, x, y, z, min_distance=2) { /** @@ -1033,10 +1152,7 @@ export async function goToPosition(bot, x, y, z, min_distance=2) { return true; } - const movements = new pf.Movements(bot); - bot.pathfinder.setMovements(movements); - - const checkProgress = () => { + const checkDigProgress = () => { if (bot.targetDigBlock) { const targetBlock = bot.targetDigBlock; const itemId = bot.heldItem ? bot.heldItem.type : null; @@ -1048,17 +1164,17 @@ export async function goToPosition(bot, x, y, z, min_distance=2) { } }; - const progressInterval = setInterval(checkProgress, 1000); + const progressInterval = setInterval(checkDigProgress, 1000); try { - await bot.pathfinder.goto(new pf.goals.GoalNear(x, y, z, min_distance)); + await goToGoal(bot, new pf.goals.GoalNear(x, y, z, min_distance)); log(bot, `You have reached at ${x}, ${y}, ${z}.`); + clearInterval(progressInterval); return true; } catch (err) { log(bot, `Pathfinding stopped: ${err.message}.`); - return false; - } finally { clearInterval(progressInterval); + return false; } } @@ -1134,45 +1250,10 @@ export async function goToPlayer(bot, username, distance=3) { return false; } - - const dontBreakBlocks = ['glass', 'glass_pane']; - const nonDestructiveMovements = new pf.Movements(bot); - nonDestructiveMovements.canOpenDoors = true; - nonDestructiveMovements.allowFreeMotion = true; - nonDestructiveMovements.digCost = 100; - nonDestructiveMovements.blocksCantBreak.add(dontBreakBlocks.map(block => mc.getBlockId(block))); - - const destructiveMovements = new pf.Movements(bot); - destructiveMovements.canOpenDoors = true; - destructiveMovements.allowFreeMotion = true; - - let movements = nonDestructiveMovements; + distance = Math.max(distance, 0.5); const goal = new pf.goals.GoalFollow(player, distance); - const timeout = 5000; - try { - console.log('finding non-destructive path...'); - const nonDestructivePath = await bot.pathfinder.getPathTo(nonDestructiveMovements, goal, timeout); - if ( - nonDestructivePath && - nonDestructivePath.path && - nonDestructivePath.path.length > 0 && - nonDestructivePath.status !== 'noPath' - ) { - console.log('found non-destructive path'); - movements = nonDestructiveMovements; - } - else { - console.log('no non-destructive path found, using destructive path'); - movements = destructiveMovements; - } - } catch (err) { - log(bot, `Could not find a path: ${err.message}.`); - return false; - } - - bot.pathfinder.setMovements(movements); - await bot.pathfinder.goto(goal, true); + await goToGoal(bot, goal, true); log(bot, `You have reached ${username}.`); } @@ -1192,24 +1273,36 @@ export async function followPlayer(bot, username, distance=4) { return false; const move = new pf.Movements(bot); + move.digCost = 10; bot.pathfinder.setMovements(move); + let doorCheckInterval = startDoorInterval(bot); + bot.pathfinder.setGoal(new pf.goals.GoalFollow(player, distance), true); log(bot, `You are now actively following player ${username}.`); + while (!bot.interrupt_code) { await new Promise(resolve => setTimeout(resolve, 500)); // in cheat mode, if the distance is too far, teleport to the player if (bot.modes.isOn('cheat') && bot.entity.position.distanceTo(player.position) > 100 && player.isOnGround) { await goToPlayer(bot, username); } - if (bot.modes.isOn('unstuck')) { - const is_nearby = bot.entity.position.distanceTo(player.position) <= distance + 1; - if (is_nearby) - bot.modes.pause('unstuck'); - else - bot.modes.unpause('unstuck'); + const is_nearby = bot.entity.position.distanceTo(player.position) <= distance + 1; + if (is_nearby) { + clearInterval(doorCheckInterval); + doorCheckInterval = null; + bot.modes.pause('unstuck'); + bot.modes.pause('elbow_room'); + } + else { + if (!doorCheckInterval) { + doorCheckInterval = startDoorInterval(bot); + } + bot.modes.unpause('unstuck'); + bot.modes.unpause('elbow_room'); } } + clearInterval(doorCheckInterval); return true; } @@ -1232,7 +1325,6 @@ export async function moveAway(bot, distance) { const move = new pf.Movements(bot); const path = await bot.pathfinder.getPathTo(move, inverted_goal, 10000); let last_move = path.path[path.path.length-1]; - console.log(last_move); if (last_move) { let x = Math.floor(last_move.x); let y = Math.floor(last_move.y); diff --git a/src/agent/modes.js b/src/agent/modes.js index dc2b925..cde53fc 100644 --- a/src/agent/modes.js +++ b/src/agent/modes.js @@ -114,6 +114,11 @@ const modes_list = [ }); } this.last_time = Date.now(); + }, + unpause: function () { + this.prev_location = null; + this.stuck_time = 0; + this.prev_dig_block = null; } }, { @@ -342,13 +347,18 @@ class ModeController { } unpause(mode_name) { - modes_map[mode_name].paused = false; + const mode = modes_map[mode_name]; + //if unpause func is defined and mode is currently paused + if (mode.unpause && mode.paused) { + mode.unpause(); + } + mode.paused = false; } unPauseAll() { for (let mode of modes_list) { if (mode.paused) console.log(`Unpausing mode ${mode.name}`); - mode.paused = false; + this.unpause(mode.name); } } From 5aa4aa4e06d77d2e33f83582aa8b986db9b6a4f4 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Thu, 14 Aug 2025 18:40:13 -0500 Subject: [PATCH 4/4] use goToGoal, better movement selection and error handling --- src/agent/library/skills.js | 74 +++++++++++++++++++------------------ src/agent/library/world.js | 1 + 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/agent/library/skills.js b/src/agent/library/skills.js index bbe8acf..9d62ef1 100644 --- a/src/agent/library/skills.js +++ b/src/agent/library/skills.js @@ -4,7 +4,7 @@ import pf from 'mineflayer-pathfinder'; import Vec3 from 'vec3'; import settings from "../../../settings.js"; -const blockPlaceDelay = settings.block_place_delay || 10; +const blockPlaceDelay = settings.block_place_delay == null ? 0 : settings.block_place_delay; const useDelay = blockPlaceDelay > 0; export function log(bot, message) { @@ -506,7 +506,7 @@ export async function pickupNearbyItems(bot) { let pickedUp = 0; while (nearestItem) { bot.pathfinder.setMovements(new pf.Movements(bot)); - await bot.pathfinder.goto(new pf.goals.GoalFollow(nearestItem, 0.8), true); + await goToGoal(bot, new pf.goals.GoalFollow(nearestItem, 0.8)); await new Promise(resolve => setTimeout(resolve, 200)); let prev = nearestItem; nearestItem = getNearestItem(bot); @@ -549,7 +549,7 @@ export async function breakBlockAt(bot, x, y, z) { movements.canPlaceOn = false; movements.allow1by1towers = false; bot.pathfinder.setMovements(movements); - await bot.pathfinder.goto(new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); + await goToGoal(bot, new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); } if (bot.game.gameMode !== 'creative') { await bot.tool.equipForBlock(block); @@ -727,7 +727,7 @@ export async function placeBlock(bot, blockType, x, y, z, placeOn='bottom', dont let pos = targetBlock.position; let movements = new pf.Movements(bot); bot.pathfinder.setMovements(movements); - await bot.pathfinder.goto(new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); + await goToGoal(bot, new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); } await bot.equip(block, 'hand'); @@ -952,6 +952,10 @@ export async function giveToPlayer(bot, itemType, username, num=1) { * @example * await skills.giveToPlayer(bot, "oak_log", "player1"); **/ + if (bot.username === username) { + log(bot, `You cannot give items to yourself.`); + return false; + } let player = bot.players[username].entity if (!player) { log(bot, `Could not find ${username}.`); @@ -1011,7 +1015,7 @@ export async function giveToPlayer(bot, itemType, username, num=1) { export async function goToGoal(bot, goal) { /** - * Navigate to the given goal, attempting minimally destructive movements. + * Navigate to the given goal. Use doors and attempt minimally destructive movements. * @param {MinecraftBot} bot, reference to the minecraft bot. * @param {pf.goals.Goal} goal, the goal to navigate to. **/ @@ -1025,30 +1029,18 @@ export async function goToGoal(bot, goal) { const destructiveMovements = new pf.Movements(bot); - let final_movements = null; - let movements = [ - {name: 'nonDestructive', movements: nonDestructiveMovements}, - {name: 'destructive', movements: destructiveMovements} - ]; + let final_movements = destructiveMovements; const pathfind_timeout = 1000; - try { - for (let i = 0; i < movements.length; i++) { - const movement = movements[i].movements; - const path = await bot.pathfinder.getPathTo(movement, goal, pathfind_timeout); - if (path && path.path && path.path.length > 0 && path.status !== 'noPath') { - final_movements = movement; - log(bot, `Using ${movements[i].name} movements.`); - break; - } - } - if (!final_movements) { - log(bot, `Could not find a path to ${username}.`); - return false; - } - } catch (err) { - log(bot, `Could not find a path: ${err.message}.`); - return false; + if (await bot.pathfinder.getPathTo(nonDestructiveMovements, goal, pathfind_timeout).status === 'success') { + final_movements = nonDestructiveMovements; + log(bot, `Found non-destructive path.`); + } + else if (await bot.pathfinder.getPathTo(destructiveMovements, goal, pathfind_timeout).status === 'success') { + log(bot, `Found destructive path.`); + } + else { + log(bot, `Could not find a path to goal, attempting to navigate anyway using destructive movements.`); } const doorCheckInterval = startDoorInterval(bot); @@ -1059,9 +1051,9 @@ export async function goToGoal(bot, goal) { clearInterval(doorCheckInterval); return true; } catch (err) { - log(bot, `Could not find a path: ${err.message}.`); clearInterval(doorCheckInterval); - return false; + // we need to catch so we can clean up the door check interval, then rethrow the error + throw err; } } @@ -1168,9 +1160,16 @@ export async function goToPosition(bot, x, y, z, min_distance=2) { try { await goToGoal(bot, new pf.goals.GoalNear(x, y, z, min_distance)); - log(bot, `You have reached at ${x}, ${y}, ${z}.`); clearInterval(progressInterval); - return true; + const distance = bot.entity.position.distanceTo(new Vec3(x, y, z)); + if (distance <= min_distance+1) { + log(bot, `You have reached at ${x}, ${y}, ${z}.`); + return true; + } + else { + log(bot, `Unable to reach ${x}, ${y}, ${z}, you are ${Math.round(distance)} blocks away.`); + return false; + } } catch (err) { log(bot, `Pathfinding stopped: ${err.message}.`); clearInterval(progressInterval); @@ -1235,7 +1234,10 @@ export async function goToPlayer(bot, username, distance=3) { * @example * await skills.goToPlayer(bot, "player"); **/ - + if (bot.username === username) { + log(bot, `You are already at ${username}.`); + return true; + } if (bot.modes.isOn('cheat')) { bot.chat('/tp @s ' + username); log(bot, `Teleported to ${username}.`); @@ -1287,7 +1289,7 @@ export async function followPlayer(bot, username, distance=4) { if (bot.modes.isOn('cheat') && bot.entity.position.distanceTo(player.position) > 100 && player.isOnGround) { await goToPlayer(bot, username); } - const is_nearby = bot.entity.position.distanceTo(player.position) <= distance + 1; + const is_nearby = bot.entity.position.distanceTo(player.position) <= distance + 2; if (is_nearby) { clearInterval(doorCheckInterval); doorCheckInterval = null; @@ -1334,7 +1336,7 @@ export async function moveAway(bot, distance) { } } - await bot.pathfinder.goto(inverted_goal); + await goToGoal(bot, inverted_goal); let new_pos = bot.entity.position; log(bot, `Moved away from nearest entity to ${new_pos}.`); return true; @@ -1528,7 +1530,7 @@ export async function tillAndSow(bot, x, y, z, seedType=null) { if (bot.entity.position.distanceTo(block.position) > 4.5) { let pos = block.position; bot.pathfinder.setMovements(new pf.Movements(bot)); - await bot.pathfinder.goto(new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); + await goToGoal(bot, new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); } if (block.name !== 'farmland') { let hoe = bot.inventory.items().find(item => item.name.includes('hoe')); @@ -1574,7 +1576,7 @@ export async function activateNearestBlock(bot, type) { if (bot.entity.position.distanceTo(block.position) > 4.5) { let pos = block.position; bot.pathfinder.setMovements(new pf.Movements(bot)); - await bot.pathfinder.goto(new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); + await goToGoal(bot, new pf.goals.GoalNear(pos.x, pos.y, pos.z, 4)); } await bot.activateBlock(block); log(bot, `Activated ${type} at x:${block.position.x.toFixed(1)}, y:${block.position.y.toFixed(1)}, z:${block.position.z.toFixed(1)}.`); diff --git a/src/agent/library/world.js b/src/agent/library/world.js index 0253b41..ae97d2d 100644 --- a/src/agent/library/world.js +++ b/src/agent/library/world.js @@ -354,6 +354,7 @@ export async function isClearPath(bot, target) { let movements = new pf.Movements(bot) movements.canDig = false; movements.canPlaceOn = false; + movements.canOpenDoors = false; let goal = new pf.goals.GoalNear(target.position.x, target.position.y, target.position.z, 1); let path = await bot.pathfinder.getPathTo(movements, goal, 100); return path.status === 'success';