const { Movements } = require('mineflayer-pathfinder') const v = require('vec3') let cfg = {} let bot = {} // let moving let pathfinder let mcData let movements = [] function initMoves(bot = bot, mcData = bot.mcData) { console.info(movements) if (movements.length > 0) { bot.pathfinder.setMovements(movements.defaultMove) return console.warn("movements already initialized!") } let defaultMove = new Movements(bot, mcData) defaultMove.canDig = false defaultMove.scafoldingBlocks.push(mcData.blocksByName.slime_block.id) defaultMove.blocksCantBreak.add(mcData.blocksByName.glass.id) defaultMove.blocksToAvoid.add(mcData.blocksByName.magma_block.id) movements.push(defaultMove) movements.defaultMove = movements[0] bot.pathfinder.setMovements(defaultMove) } function moveNear(pos, distance = 3) { const { GoalNear } = require('mineflayer-pathfinder').goals pos = v(pos) cfg.quiet || bot.chat(`moving to ${pos.floored()}`) bot.pathfinder.setMovements(movements.defaultMove) bot.pathfinder.setGoal(new GoalNear(pos.x, pos.y, pos.z, distance)) } function moveXZ(pos) { const { GoalXZ } = require('mineflayer-pathfinder').goals if (Array.isArray(pos) && pos.length == 2) { pos = v(pos[0], 0, pos[1]) } pos = v(pos) console.log(pos) cfg.quiet || bot.chat(`moving to ${pos.floored()}`) bot.pathfinder.setMovements(movements.defaultMove) bot.pathfinder.setGoal(new GoalXZ(pos.x, pos.z)) } function moveY(pos) { const { GoalY } = require('mineflayer-pathfinder').goals if (Array.isArray(pos) && pos.length == 1) { pos = v(null, pos[0], null) } pos = v(pos) console.log(pos) cfg.quiet || bot.chat(`moving to ${pos.floored()}`) bot.pathfinder.setMovements(movements.defaultMove) bot.pathfinder.setGoal(new GoalY(pos.y)) } function follow(entity, dynamic = true, distance = 3) { console.assert(entity) const { GoalFollow } = require('mineflayer-pathfinder').goals cfg.quiet && console.log(entity) || bot.chat( `following ${entity.type }: ${entity.username || entity.displayName }${dynamic ? "" : " once"}` ) entity = entity.entity ? entity.entity : entity // console.log(entity) bot.pathfinder.setMovements(movements.defaultMove) bot.pathfinder.setGoal(new GoalFollow(entity, distance), dynamic) } function ride(entity) { entity = entity?.entity || entity const ridableMobs = ["Horse", "Donkey", "Pig", "Strider"] const vehicle = entity ? entity : bot.nearestEntity(e => { const maybeRidableMob = e.mobType?.split(" ") return e.kind == "Vehicles" || ridableMobs.includes(e.mobType) || ridableMobs.includes(maybeRidableMob[maybeRidableMob.length - 1]) }) if (!vehicle) { return cfg.quiet || bot.chat(`Nothing to ${message_parts[0]}`) } else if ((dist = bot.entity.position.distanceSquared(vehicle.position)) > 36) { bot.lookAt(vehicle.position) follow(vehicle, false) bot.once('goal_reached', ride) return cfg.quiet || bot.chat(`${vehicle.name} bit far`) } console.log("vehicle:", vehicle) bot.mount(vehicle) } function hit(blockOrEntity) { bot.chat(`hitting ${entity.name || entity.type}`) } function goalReached(goal) { console.log(goal) const entity = goal?.entity let entityInfo = "" if (entity) { entityInfo += entity.type + ": " switch (entity.type) { case "player": entityInfo += entity.username break; default: break; } } cfg.quiet || bot.chat(`goal reached: ${entityInfo}; pos: [x:${goal?.x}, y:${goal?.y}, z:${goal?.z}]`) } function stop() { bot.pathfinder.setGoal(null) bot.stopDigging() } function command(message_parts, player) { const message_parts2 = message_parts.slice(1) switch (message_parts[0]) { case "init": initMoves() break case "near": switch (message_parts2.length) { case 0: moveNear(bot.nearestEntity().position) break case 1: switch (message_parts2[0]) { case "me": if (player) { moveNear(player.position) } else { cfg.quiet || bot.chat("can't see you") } break; default: const aPlayer = bot.players[message_parts2[0]] ? bot.players[message_parts2[0]].entity : null if (aPlayer) { moveNear(aPlayer.position) } else { cfg.quiet || bot.chat(`can't see ${message_parts2[0]}`) } break; } break case 2: //TODO this isn't near moveXZ(message_parts2) break case 3: //TODO more checks moveNear(message_parts2) break default: break } break case "follow": // message_parts2 = message_parts.slice(2) switch (message_parts2.length) { case 0: follow(bot.nearestEntity()) break case 1: let dist = 3 switch (message_parts2[0]) { case "close": dist = 1 case "me": case "once": if (player) { follow(player, message_parts2[0] === "me", dist) } else { cfg.quiet || bot.chat("can't see you") } break; default: const aPlayer = bot.players[message_parts2[0]] ? bot.players[message_parts2[0]].entity : null if (aPlayer) { follow(aPlayer) } else { cfg.quiet || bot.chat(`can't see ${message_parts2[0]}`) } break; } break // case 2: // bot.lookAt({}) goalxz? // break // case 3: //TODO more checks // moveNear(message_parts2) // break default: cfg.quiet || bot.chat("unknown or bad command") break } break case "ride": case "mount": ride() break case "w": case "f": command(["forward"].concat(message_parts.slice(1))) break case "s": case "b": command(["back"].concat(message_parts.slice(1))) break case "a": case "l": command(["right"].concat(message_parts.slice(1))) break case "d": case "r": command(["left"].concat(message_parts.slice(1))) break case "back": case "forward": case "jump": case "left": case "right": case "sneak": case "sprint": console.info(bot.controlState[message_parts[0]], bot.entity.position.floored()) bot.setControlState(message_parts[0], true) console.info(bot.controlState[message_parts[0]]) setTimeout(bot.setControlState, 200 * (message_parts[1] || 1), message_parts[0], false) setTimeout(console.info, 5000, bot.controlState[message_parts[0]], bot.entity.position.floored()) break case "stop": stop() break default: return cfg.quiet || bot.chat(`unknown command ${message_parts[0]}`) } } const load = (config) => { cfg = config bot = cfg.bot cfg.move = { // auto: true, canDig: false, // list: ["hello", "wassup"], quiet: !!cfg.quiet, movements: [] } mcData = bot.mcData || (bot.mcData = require('minecraft-data')(bot.version)) pathfinder = bot.pathfinder || bot.loadPlugin(require('mineflayer-pathfinder').pathfinder) // initMoves(bot, mcData) setTimeout(initMoves, 500, bot, mcData) bot.on('goal_reached', goalReached) } const unload = () => { stop() bot.off('goal_reached', goalReached) } module.exports = { load, unload, command, stop, initMoves, moveNear, moveXZ, moveY, follow, ride }