[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n- package-ecosystem: npm\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-limit: 10\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [24.x]\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Use Node.js ${{ matrix.node-version }}\n      uses: actions/setup-node@v1\n      with:\n        node-version: ${{ matrix.node-version }}\n    - run: npm install\n    - run: npm test"
  },
  {
    "path": ".github/workflows/commands.yml",
    "content": "name: Repo Commands\n\non:\n  issue_comment:        # Handle comment commands\n    types: [created]\n  pull_request:         # Handle renamed PRs\n    types: [edited]\npermissions:\n  contents: write\n\njobs:\n  comment-trigger:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Check out repository\n      uses: actions/checkout@v3\n    - name: Run command handlers\n      uses: PrismarineJS/prismarine-repo-actions@master\n      with:\n        token: ${{ secrets.PAT_PASSWORD }}\n        install-command: npm install\n        /fixlint.fix-command: npm run fix\n"
  },
  {
    "path": ".github/workflows/npm-publish.yml",
    "content": "name: npm-publish\non:\n  push:\n    branches:\n      - master # Change this to your default branch\njobs:\n  npm-publish:\n    name: npm-publish\n    runs-on: ubuntu-latest\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@master\n    - name: Set up Node.js\n      uses: actions/setup-node@master\n      with:\n        node-version: 14.0.0\n    - id: publish\n      uses: JS-DevTools/npm-publish@v1\n      with:\n        token: ${{ secrets.NPM_AUTH_TOKEN }}\n    - name: Create Release\n      if: steps.publish.outputs.type != 'none'\n      id: create_release\n      uses: actions/create-release@v1\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      with:\n        tag_name: ${{ steps.publish.outputs.version }}\n        release_name: Release ${{ steps.publish.outputs.version }}\n        body: ${{ steps.publish.outputs.version }}\n        draft: false\n        prerelease: false\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\npackage-lock.json\nyarn.lock\n.vscode\n"
  },
  {
    "path": ".gitpod.yml",
    "content": "tasks:\n- command: npm install\n"
  },
  {
    "path": ".npmrc",
    "content": "package-lock=false\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Karang\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/bench.js",
    "content": "// Simple test to evaluate how much time it takes to find a path of 100 blocks\n\nconst mineflayer = require('mineflayer')\nconst { pathfinder, Movements } = require('mineflayer-pathfinder')\nconst { GoalXZ } = require('mineflayer-pathfinder').goals\nconst { performance } = require('perf_hooks')\n\nif (process.argv.length > 6) {\n  console.log('Usage : node bench.js [<host>] [<port>] [<name>] [<password>]')\n  process.exit(1)\n}\n\nconst bot = mineflayer.createBot({\n  host: process.argv[2] || 'localhost',\n  port: parseInt(process.argv[3]) || 25565,\n  username: process.argv[4] || 'bench',\n  password: process.argv[5]\n})\n\nbot.loadPlugin(pathfinder)\n\nbot.on('error', (err) => console.log(err))\n\nconst createTime = performance.now()\nbot.once('spawn', () => {\n  console.log('Spawning took ' + (performance.now() - createTime).toFixed(2) + ' ms.')\n\n  const defaultMove = new Movements(bot)\n  const goal = new GoalXZ(bot.entity.position.x + 100, bot.entity.position.z)\n  const results = bot.pathfinder.getPathTo(defaultMove, goal, 10000)\n  console.log('I can get there in ' + results.path.length + ' moves. Computation took ' + results.time.toFixed(2) + ' ms.')\n\n  bot.quit()\n  process.exit()\n})\n"
  },
  {
    "path": "examples/blockInteraction.js",
    "content": "const mineflayer = require('mineflayer')\nconst { pathfinder, Movements } = require('mineflayer-pathfinder')\nconst { GoalNear, GoalBlock, GoalXZ, GoalY, GoalFollow, GoalPlaceBlock, GoalLookAtBlock } = require('mineflayer-pathfinder').goals\nconst Vec3 = require('vec3').Vec3\n\nif (process.argv.length > 6) {\n  console.log('Usage : node blockInteraction.js [<host>] [<port>] [<name>] [<password>]')\n  process.exit(1)\n}\n\nconst bot = mineflayer.createBot({\n  host: process.argv[2] || 'localhost',\n  port: parseInt(process.argv[3]) || 25565,\n  username: process.argv[4] || 'blockPlacer',\n  password: process.argv[5]\n})\n\nbot.once('spawn', () => {\n  console.info('Joined the server')\n\n  bot.loadPlugin(pathfinder)\n  const defaultMove = new Movements(bot)\n  bot.pathfinder.setMovements(defaultMove)\n\n  bot.on('chat', async (username, message) => {\n    const target = bot.players[username].entity\n\n    if (message.startsWith('place')) {\n      const [, itemName] = message.split(' ')\n      if (!target) {\n        bot.chat('I can\\'t see you')\n        return\n      }\n      const itemsInInventory = bot.inventory.items().filter(item => item.name.includes(itemName))\n      if (itemsInInventory.length === 0) {\n        bot.chat('I dont have ' + itemName)\n        return\n      }\n\n      try {\n        const rayBlock = rayTraceEntitySight(target)\n        if (!rayBlock) {\n          bot.chat('Block is out of reach')\n          return\n        }\n        const face = directionToVector(rayBlock.face)\n        await bot.pathfinder.goto(new GoalPlaceBlock(rayBlock.position.offset(face.x, face.y, face.z), bot.world, {\n          range: 4\n        }))\n        await bot.equip(itemsInInventory[0], 'hand')\n        await bot.lookAt(rayBlock.position.offset(face.x * 0.5 + 0.5, face.y * 0.5 + 0.5, face.z * 0.5 + 0.5))\n        await bot.placeBlock(rayBlock, face)\n      } catch (e) {\n        console.error(e)\n      }\n    } else if (message.startsWith('break')) {\n      if (!target) {\n        bot.chat('I can\\'t see you')\n        return\n      }\n\n      try {\n        const rayBlock = rayTraceEntitySight(target)\n        if (!rayBlock) {\n          bot.chat('Block is out of reach')\n          return\n        }\n        await bot.pathfinder.goto(new GoalLookAtBlock(rayBlock.position, bot.world, { range: 4 }))\n        const bestHarvestTool = bot.pathfinder.bestHarvestTool(bot.blockAt(rayBlock.position))\n        if (bestHarvestTool) await bot.equip(bestHarvestTool, 'hand')\n        await bot.dig(bot.blockAt(rayBlock.position), true, 'raycast')\n      } catch (e) {\n        console.error(e)\n      }\n    } else if (message === 'come') {\n      if (!target) {\n        bot.chat('I don\\'t see you !')\n        return\n      }\n      const p = target.position\n\n      bot.pathfinder.setMovements(defaultMove)\n      bot.pathfinder.setGoal(new GoalNear(p.x, p.y, p.z, 1))\n    } else if (message === 'stop') {\n      bot.pathfinder.stop()\n    } else if (message === 'follow') {\n      bot.pathfinder.setMovements(defaultMove)\n      bot.pathfinder.setGoal(new GoalFollow(target, 1), true)\n      // follow is a dynamic goal: setGoal(goal, dynamic=true)\n      // when reached, the goal will stay active and will not\n      // emit an event\n    } else if (message.startsWith('goto')) {\n      const cmd = message.split(' ')\n\n      if (cmd.length === 4) { // goto x y z\n        const x = parseInt(cmd[1], 10)\n        const y = parseInt(cmd[2], 10)\n        const z = parseInt(cmd[3], 10)\n\n        bot.pathfinder.setMovements(defaultMove)\n        bot.pathfinder.setGoal(new GoalBlock(x, y, z))\n      } else if (cmd.length === 3) { // goto x z\n        const x = parseInt(cmd[1], 10)\n        const z = parseInt(cmd[2], 10)\n\n        bot.pathfinder.setMovements(defaultMove)\n        bot.pathfinder.setGoal(new GoalXZ(x, z))\n      } else if (cmd.length === 2) { // goto y\n        const y = parseInt(cmd[1], 10)\n\n        bot.pathfinder.setMovements(defaultMove)\n        bot.pathfinder.setGoal(new GoalY(y))\n      }\n    }\n  })\n\n  const rayTraceEntitySight = function (entity) {\n    if (bot.world?.raycast) {\n      const { height, position, yaw, pitch } = entity\n      const x = -Math.sin(yaw) * Math.cos(pitch)\n      const y = Math.sin(pitch)\n      const z = -Math.cos(yaw) * Math.cos(pitch)\n      const rayBlock = bot.world.raycast(position.offset(0, height, 0), new Vec3(x, y, z), 120)\n      if (rayBlock) {\n        return rayBlock\n      }\n    } else {\n      throw Error('bot.world.raycast does not exists. Try updating prismarine-world.')\n    }\n  }\n})\n\nbot.on('error', console.error)\nbot.on('kicked', console.error)\n\nfunction directionToVector (dir) {\n  if (dir > 5 || dir < 0) return null\n  if (dir === 0) {\n    return new Vec3(0, -1, 0)\n  } else if (dir === 1) {\n    return new Vec3(0, 1, 0)\n  } else if (dir === 2) {\n    return new Vec3(0, 0, -1)\n  } else if (dir === 3) {\n    return new Vec3(0, 0, 1)\n  } else if (dir === 4) {\n    return new Vec3(-1, 0, 0)\n  } else if (dir === 5) {\n    return new Vec3(1, 0, 0)\n  }\n}\n"
  },
  {
    "path": "examples/callback.js",
    "content": "// This example uses promises instead of events like \"goal_reached\" for a cleaner look\n\nconst mineflayer = require('mineflayer')\nconst pathfinder = require('mineflayer-pathfinder').pathfinder\nconst Movements = require('mineflayer-pathfinder').Movements\nconst { GoalNear } = require('mineflayer-pathfinder').goals\nconst bot = mineflayer.createBot({ username: 'Player' })\n\n// Load plugins\nbot.loadPlugin(pathfinder)\n\nbot.once('spawn', () => {\n  // Set pathfinder movements\n  const defaultMove = new Movements(bot)\n  bot.pathfinder.setMovements(defaultMove)\n\n  bot.on('chat', async (username, message) => {\n    // If username is the same as the Bot's username, don't continue\n    if (username === bot.username) return\n\n    // Only continue when the message is \"come\"\n    if (message === 'come') {\n      const target = bot.players[username] ? bot.players[username].entity : null\n\n      // If Player doesn't exist, don't continue\n      if (!target) return bot.chat(\"I don't see you !\")\n\n      // Await pathfinder to complete the goal, then move to bot.chat and print \"I've arrived !\"\n      bot.pathfinder.goto(new GoalNear(target.position.x, target.position.y, target.position.z, 1)).then(announceArrived)\n    }\n  })\n\n  function announceArrived () {\n    const botPosition = bot.entity.position\n    bot.chat(`I've arrived, I'm at ${botPosition.x}, ${botPosition.y}, ${botPosition.z}!`)\n  }\n})\n"
  },
  {
    "path": "examples/chaining-goals.js",
    "content": "/* Pathfinder Chaining Goals example\n\nThis example shows how to chain goals together.\nRun this example with:\n\nnode examples/chaining-goals.js [host] [port] [mail/username] [is online `true`]\n\nIf you want to connect to an offline server use a username instead of an email\nand no password. If you want to join a online server use your email and follow\nthe instructions in the command line to authenticated with microsoft auth.\n\nIn Game Chat commands:\ncome\n  - Path finds to the chatting player's position when in render distance.\n\nfollow\n  - Follows the chatting player's entity until `stop` is chatted\n\nstop\n  - Stops the bot from following or path finding\n\npoint\n  - Set a checkpoint at the chatting player's position\n\nwalk\n  - Walk to all set checkpoints\n*/\n\n// Import all the modules we need\nconst mineflayer = require('mineflayer')\nconst { pathfinder, Movements } = require('mineflayer-pathfinder')\nconst {\n  GoalNear, GoalBlock, GoalFollow\n} = require('mineflayer-pathfinder').goals\n\nif (process.argv.length > 6) {\n  console.log('Usage : node chaining-goals.js [<host>] [<port>] ' +\n    '[<microsoft email/name>] [<is online `true`>]')\n  process.exit(1)\n}\n\n// Create the bot\nconst bot = mineflayer.createBot({\n  host: process.argv[2] || 'localhost',\n  port: parseInt(process.argv[3]) || 25565,\n  username: process.argv[4] || 'checkpointBot',\n  // to join offline servers auth type has to be 'mojang' (???)\n  auth: process.argv[5] === 'true' ? 'microsoft' : 'mojang',\n  // Skip validation when joining a offline server\n  skipValidation: process.argv[5] !== 'true'\n})\n\n// Load the pathfinder plugin\nbot.loadPlugin(pathfinder)\n\n// Wait for the bot to spawn in the world\nbot.once('spawn', () => {\n  // We create different movement generators for different type of activity\n  const defaultMove = new Movements(bot)\n  bot.pathfinder.setMovements(defaultMove)\n\n  // Print debug messages when the path changes\n  bot.on('path_update', (r) => {\n    const nodesPerTick = (r.visitedNodes * 50 / r.time).toFixed(2)\n    console.log(`I can get there in ${r.path.length} moves. ` +\n      `Computation took ${r.time.toFixed(2)} ms (${r.visitedNodes} nodes` +\n      `, ${nodesPerTick} nodes/tick)`)\n  })\n\n  bot.on('goal_reached', (goal) => {\n    console.log('Here I am !')\n  })\n\n  bot.on('path_reset', (reason) => {\n    console.log(`Path was reset for reason: ${reason}`)\n  })\n\n  let checkpoints = []\n\n  // Make pathfinder walk to all checkpoints in order\n  async function walkCheckpoints () {\n    if (checkpoints.length === 0) {\n      bot.chat('There are no checkpoints')\n      return\n    }\n\n    // Remove all checkpoints when starting to walking\n    const checkPointCopy = [...checkpoints]\n    checkpoints = []\n    for (const checkpoint of checkPointCopy) {\n      // Make a new goal to goto. GoalBlock will make the bot walk to the\n      // block position off checkpoint.\n      const goal = new GoalBlock(checkpoint.x, checkpoint.y, checkpoint.z)\n      try {\n        // Use await to make sure the bot is at the checkpoint before moving on\n        await bot.pathfinder.goto(goal)\n      } catch (error) {\n        console.log('Got error from goto', error.message)\n        // If we get an error we quit the loop\n        return\n      }\n    }\n  }\n\n  // Listen for chat messages chatted by other players\n  // Note: This may not work on every server as mineflayer uses regex to match\n  // chat messages. Some servers may use chat messages that do not match the\n  // regex.\n  bot.on('chat', (username, message) => {\n    if (username === bot.username) return // Ignore our own messages\n\n    // Get the player entity from the username.\n    // Note: This might not work on some servers where the players nametag name\n    // dose not match the chat message name.\n    const target = bot.players[username] ? bot.players[username].entity : null\n    if (message === 'come') {\n      if (!target) {\n        bot.chat('I don\\'t see you !')\n        return\n      }\n      const p = target.position\n\n      bot.pathfinder.setGoal(new GoalNear(p.x, p.y, p.z, 1))\n    } else if (message === 'follow') {\n      bot.pathfinder.setGoal(new GoalFollow(target, 3), true)\n      // follow is a dynamic goal: setGoal(goal, dynamic=true)\n      // when reached, the goal will stay active and will not\n      // emit an event\n    } else if (message === 'stop') {\n      bot.pathfinder.stop() // Also resets the current goal\n    } else if (message === 'point') {\n      if (!target) {\n        bot.chat('I don\\'t see you !')\n        return\n      }\n      const pos = target.position.floored()\n      checkpoints.push(pos)\n      bot.chat(`Checkpoint ${pos} set`)\n    } else if (message === 'walk') {\n      walkCheckpoints()\n        .then(() => {\n          bot.chat('Done')\n        })\n        .catch(console.error)\n    }\n  })\n})\n"
  },
  {
    "path": "examples/example.js",
    "content": "const mineflayer = require('mineflayer')\nconst { pathfinder, Movements } = require('mineflayer-pathfinder')\nconst { GoalNear, GoalBlock, GoalXZ, GoalY, GoalInvert, GoalFollow, GoalBreakBlock } = require('mineflayer-pathfinder').goals\n\nif (process.argv.length > 6) {\n  console.log('Usage : node example.js [<host>] [<port>] [<name>] [<password>]')\n  process.exit(1)\n}\n\nconst bot = mineflayer.createBot({\n  host: process.argv[2] || 'localhost',\n  port: parseInt(process.argv[3]) || 25565,\n  username: process.argv[4] || 'pathfinder',\n  password: process.argv[5]\n})\n\nbot.loadPlugin(pathfinder)\n\nbot.once('spawn', () => {\n  // We create different movement generators for different type of activity\n  const defaultMove = new Movements(bot)\n\n  bot.on('path_update', (r) => {\n    const nodesPerTick = (r.visitedNodes * 50 / r.time).toFixed(2)\n    console.log(`I can get there in ${r.path.length} moves. Computation took ${r.time.toFixed(2)} ms (${r.visitedNodes} nodes, ${nodesPerTick} nodes/tick)`)\n  })\n\n  bot.on('goal_reached', (goal) => {\n    console.log('Here I am !')\n  })\n\n  bot.on('path_reset', (reason) => {\n    console.log(`Path was reset for reason: ${reason}`)\n  })\n\n  bot.on('chat', (username, message) => {\n    if (username === bot.username) return\n\n    const target = bot.players[username] ? bot.players[username].entity : null\n    if (message === 'come') {\n      if (!target) {\n        bot.chat('I don\\'t see you !')\n        return\n      }\n      const p = target.position\n\n      bot.pathfinder.setMovements(defaultMove)\n      bot.pathfinder.setGoal(new GoalNear(p.x, p.y, p.z, 1))\n    } else if (message.startsWith('goto')) {\n      const cmd = message.split(' ')\n\n      if (cmd.length === 4) { // goto x y z\n        const x = parseInt(cmd[1], 10)\n        const y = parseInt(cmd[2], 10)\n        const z = parseInt(cmd[3], 10)\n\n        bot.pathfinder.setMovements(defaultMove)\n        bot.pathfinder.setGoal(new GoalBlock(x, y, z))\n      } else if (cmd.length === 3) { // goto x z\n        const x = parseInt(cmd[1], 10)\n        const z = parseInt(cmd[2], 10)\n\n        bot.pathfinder.setMovements(defaultMove)\n        bot.pathfinder.setGoal(new GoalXZ(x, z))\n      } else if (cmd.length === 2) { // goto y\n        const y = parseInt(cmd[1], 10)\n\n        bot.pathfinder.setMovements(defaultMove)\n        bot.pathfinder.setGoal(new GoalY(y))\n      }\n    } else if (message === 'follow') {\n      bot.pathfinder.setMovements(defaultMove)\n      bot.pathfinder.setGoal(new GoalFollow(target, 3), true)\n      // follow is a dynamic goal: setGoal(goal, dynamic=true)\n      // when reached, the goal will stay active and will not\n      // emit an event\n    } else if (message === 'avoid') {\n      bot.pathfinder.setMovements(defaultMove)\n      bot.pathfinder.setGoal(new GoalInvert(new GoalFollow(target, 5)), true)\n    } else if (message === 'stop') {\n      bot.pathfinder.stop()\n    } else if (message === 'break') {\n      if (!target) {\n        bot.chat('I can\\'t see you!')\n        return\n      }\n      const p = target.position.offset(0, -1, 0)\n      const goal = new GoalBreakBlock(p.x, p.y, p.z, bot)\n      bot.pathfinder.goto(goal)\n        .then(() => {\n          bot.dig(bot.blockAt(p), 'raycast')\n            .catch(err => console.error('digging error', err))\n        }, (err) => {\n          console.error('Pathfing error', err)\n        })\n    }\n  })\n})\n"
  },
  {
    "path": "examples/exclusionArea.js",
    "content": "/* Pathfinder Exclusion Area example\n\nThis example shows the use of exclusion areas with the Movement Class.\n\nIn Game Chat commands:\ncome\n  - Path finds to the chatting player's position when in render distance.\nexclude this (break | step | place) <radius>\n  - Exclude a spherical area off size <radius> of type break, step or place at the chatting\n  player's position  when in render distance\ngoto (x y z) | (x z) | y\n  - Goto a specific coordinate\nfollow\n  - Follows the chatting player's entity until stop is chatted\nstop\n  - Stops the bot from following or path finding\n*/\n\nconst mineflayer = require('mineflayer')\nconst { pathfinder, Movements } = require('mineflayer-pathfinder')\nconst { GoalNear, GoalBlock, GoalXZ, GoalY, GoalFollow } = require('mineflayer-pathfinder').goals\n\nif (process.argv.length > 6) {\n  console.log('Usage : node example.js [<host>] [<port>] [<name>] [<password>]')\n  process.exit(1)\n}\n\nconst bot = mineflayer.createBot({\n  host: process.argv[2] || 'localhost',\n  port: parseInt(process.argv[3]) || 25565,\n  username: process.argv[4] || 'exclusionAreaBot',\n  password: process.argv[5]\n})\n\nbot.loadPlugin(pathfinder)\n\nbot.once('spawn', () => {\n  // We create different movement generators for different type of activity\n  const defaultMove = new Movements(bot)\n  bot.pathfinder.setMovements(defaultMove)\n\n  bot.on('path_update', (r) => {\n    const nodesPerTick = (r.visitedNodes * 50 / r.time).toFixed(2)\n    console.log(`I can get there in ${r.path.length} moves. ` +\n      `Computation took ${r.time.toFixed(2)} ms (${r.visitedNodes} nodes` +\n      `, ${nodesPerTick} nodes/tick)`)\n  })\n\n  bot.on('goal_reached', (goal) => {\n    console.log('Here I am !')\n  })\n\n  bot.on('path_reset', (reason) => {\n    console.log(`Path was reset for reason: ${reason}`)\n  })\n\n  bot.on('chat', (username, message) => {\n    if (username === bot.username) return\n\n    const target = bot.players[username] ? bot.players[username].entity : null\n    if (message === 'come') {\n      if (!target) {\n        bot.chat('I don\\'t see you !')\n        return\n      }\n      const p = target.position\n\n      bot.pathfinder.setGoal(new GoalNear(p.x, p.y, p.z, 1))\n    } else if (message.startsWith('exclude')) {\n      const cmd = message.split(' ')\n      if (cmd[1] === 'this') {\n        if (!target) {\n          bot.chat('I can\\'t see you')\n          return\n        }\n        const type = cmd[2].trim()\n        if (!['break', 'step', 'place'].includes(type.toLowerCase())) {\n          return bot.chat('type must be \"break\", \"step\" or \"place\"')\n        }\n        const radius = Number(cmd[3])\n        const center = target.position.floored()\n        if (isNaN(radius)) return bot.chat('Radius must be a number')\n        // Import typings for intellisense\n        /**\n         * @param {import('mineflayer-pathfinder').SafeBlock} block block */\n        const isExcluded = (block) => {\n          return block.position.distanceTo(center) <= radius ? 0 : 100\n        }\n        switch (type.toLowerCase()) {\n          case 'step':\n            bot.pathfinder.movements.exclusionAreasStep.push(isExcluded)\n            break\n          case 'break':\n            bot.pathfinder.movements.exclusionAreasBreak.push(isExcluded)\n            break\n          case 'place':\n            bot.pathfinder.movements.exclusionAreasPlace.push(isExcluded)\n            break\n        }\n        // At 5. The bot avoids the area most of the time but can still move into and out of it.\n        bot.pathfinder.movements.exclusionAreaPower = 5\n        bot.pathfinder.setMovements(bot.pathfinder.movements)\n        bot.chat(`Added exclusion area circle around ${center.toString()} with radius ${radius}`)\n      } else {\n        bot.chat('Usage: exclude this (break | step | place) <radius>')\n      }\n    } else if (message.startsWith('goto')) {\n      const cmd = message.split(' ')\n\n      if (cmd.length === 4) { // goto x y z\n        const x = parseInt(cmd[1], 10)\n        const y = parseInt(cmd[2], 10)\n        const z = parseInt(cmd[3], 10)\n\n        bot.pathfinder.setGoal(new GoalBlock(x, y, z))\n      } else if (cmd.length === 3) { // goto x z\n        const x = parseInt(cmd[1], 10)\n        const z = parseInt(cmd[2], 10)\n\n        bot.pathfinder.setGoal(new GoalXZ(x, z))\n      } else if (cmd.length === 2) { // goto y\n        const y = parseInt(cmd[1], 10)\n\n        bot.pathfinder.setGoal(new GoalY(y))\n      }\n    } else if (message === 'follow') {\n      bot.pathfinder.setGoal(new GoalFollow(target, 3), true)\n      // follow is a dynamic goal: setGoal(goal, dynamic=true)\n      // when reached, the goal will stay active and will not\n      // emit an event\n    } else if (message === 'stop') {\n      bot.pathfinder.stop() // Also resets the current goal\n    }\n  })\n})\n"
  },
  {
    "path": "examples/movements.js",
    "content": "/*\n * This example demonstrates how easy it is to change the default movement\n *\n * Below are a few options you can edit in the Movement Class\n * but remember to check out the API documentation to find even more!\n *\n * This bot also follows a player when called called out to it.\n */\n\nconst mineflayer = require('mineflayer')\nconst { pathfinder, Movements } = require('mineflayer-pathfinder')\nconst { GoalNear } = require('mineflayer-pathfinder').goals\n\nconst bot = mineflayer.createBot({\n  host: process.argv[2],\n  port: parseInt(process.argv[3]),\n  username: process.argv[4] ? process.argv[4] : 'movementsbot',\n  password: process.argv[5]\n})\n\nbot.loadPlugin(pathfinder)\n\nbot.once('spawn', () => {\n  /*\n   * pathfinder comes with default moves preinitialized (a instance of the movement class)\n   * the moves come with default logic, like how much it can fall\n   * what blocks are used to scaffold, and what blocks to avoid.\n   */\n\n  // To get started create a instance of the Movements class\n  const customMoves = new Movements(bot)\n  // To make changes to the behaviour, customize the properties of the instance\n  customMoves.canDig = false\n  customMoves.allow1by1towers = false\n  customMoves.scafoldingBlocks.push(bot.registry.itemsByName.stone.id)\n  // Thing to note scaffoldingBlocks are an array while other namespaces are usually sets\n  customMoves.blocksToAvoid.add(bot.registry.blocksByName.carrot.id)\n\n  // To initialize the new movements use the .setMovements method.\n  bot.pathfinder.setMovements(customMoves)\n\n  bot.on('chat', function (username, message) {\n    if (username === bot.username) return\n\n    if (message === 'come') {\n      const target = bot.players[username]?.entity\n      if (!target) {\n        bot.chat('I don\\'t see you !')\n        return\n      }\n      const p = target.position\n\n      bot.pathfinder.setGoal(new GoalNear(p.x, p.y, p.z, 1))\n    }\n  })\n})\n"
  },
  {
    "path": "examples/multiple.js",
    "content": "const mineflayer = require('mineflayer')\nconst { pathfinder, Movements } = require('mineflayer-pathfinder')\nconst { GoalInvert, GoalFollow } = require('mineflayer-pathfinder').goals\n\nmineflayer.multiple = (bots, constructor) => {\n  const { Worker, isMainThread, workerData } = require('worker_threads')\n  if (isMainThread) {\n    const threads = []\n    for (const i in bots) {\n      threads.push(new Worker(__filename, { workerData: bots[i] }))\n    }\n  } else {\n    constructor(workerData)\n  }\n}\n\nconst bots = []\nfor (let i = 0; i < 40; i++) {\n  bots.push({ username: `Bot${i}` })\n}\n\nmineflayer.multiple(bots, ({ username }) => {\n  const bot = mineflayer.createBot({ username, viewDistance: 'tiny' })\n\n  bot.loadPlugin(pathfinder)\n\n  bot.once('spawn', () => {\n    // We create different movement generators for different type of activity\n    const defaultMove = new Movements(bot)\n    defaultMove.allowFreeMotion = true\n    bot.pathfinder.searchRadius = 10\n\n    bot.on('path_update', (results) => {\n      console.log('[' + username + '] I can get there in ' + results.path.length + ' moves. Computation took ' + results.time.toFixed(2) + ' ms.')\n    })\n\n    bot.on('goal_reached', (goal) => {\n      console.log('[' + username + '] Here I am !')\n    })\n\n    bot.on('chat', (username, message) => {\n      if (username === bot.username) return\n\n      const target = bot.players[username].entity\n      if (message === 'follow') {\n        bot.pathfinder.setMovements(defaultMove)\n        bot.pathfinder.setGoal(new GoalFollow(target, 5), true)\n      } else if (message === 'avoid') {\n        bot.pathfinder.setMovements(defaultMove)\n        bot.pathfinder.setGoal(new GoalInvert(new GoalFollow(target, 5)), true)\n      } else if (message === 'stop') {\n        bot.pathfinder.setGoal(null)\n      }\n    })\n  })\n})\n"
  },
  {
    "path": "examples/promise.js",
    "content": "// This example uses promises instead of events like \"goal_reached\" for a cleaner look\n\nconst mineflayer = require('mineflayer')\nconst pathfinder = require('mineflayer-pathfinder').pathfinder\nconst Movements = require('mineflayer-pathfinder').Movements\nconst { GoalNear } = require('mineflayer-pathfinder').goals\nconst bot = mineflayer.createBot({ username: 'Player' })\n\n// Load pathfinder\nbot.loadPlugin(pathfinder)\n\nbot.once('spawn', () => {\n  // Set pathfinder movements\n  const defaultMove = new Movements(bot)\n  bot.pathfinder.setMovements(defaultMove)\n\n  bot.on('chat', async (username, message) => {\n    // If username is the same as the Bot's username, don't continue\n    if (username === bot.username) return\n\n    // Only continue when the message is \"come\"\n    if (message === 'come') {\n      const target = bot.players[username] ? bot.players[username].entity : null\n\n      // If Player doesn't exist, don't continue\n      if (!target) return bot.chat(\"I don't see you !\")\n\n      // Await pathfinder to complete the goal, then move to bot.chat and print \"I've arrived !\"\n      await bot.pathfinder.goto(new GoalNear(target.position.x, target.position.y, target.position.z, 1))\n      bot.chat(\"I've arrived!\")\n    }\n  })\n})\n"
  },
  {
    "path": "examples/tutorial/basic.js",
    "content": "/*\r\n * This example shows the usage of the GoalBlock\r\n * goal for mineflayer-pathfinder\r\n *\r\n * See a more detailed explanation here:\r\n * https://github.com/PrismarineJS/mineflayer-pathfinder/blob/master/examples/tutorial/goalsExplained.md\r\n *\r\n * Made by Jovan04 06/07/2023\r\n*/\r\n\r\nconst mineflayer = require('mineflayer') // import mineflayer, pathfinder, the Movements class, and our goal(s)\r\nconst { pathfinder, Movements, goals: { GoalBlock } } = require('mineflayer-pathfinder')\r\n\r\nconst bot = mineflayer.createBot({ // create our bot\r\n  host: 'localhost',\r\n  port: 25565,\r\n  username: 'Pathfinder',\r\n  auth: 'offline'\r\n})\r\n\r\nbot.once('spawn', () => {\r\n  bot.loadPlugin(pathfinder) // load pathfinder plugin into the bot\r\n  const defaultMovements = new Movements(bot) // create a new instance of the `Movements` class\r\n  bot.pathfinder.setMovements(defaultMovements) // set the bot's movements to the `Movements` we just created\r\n})\r\n\r\nbot.on('chat', async (username, message) => {\r\n  if (username === bot.username) return // make bot ignore its own messages\r\n\r\n  if (message === 'go') { // this is our trigger message (only works on servers with vanilla chat)\r\n    bot.chat('Going to my goal!')\r\n    const myGoal = new GoalBlock(15, 3, 75)\r\n    await bot.pathfinder.goto(myGoal)\r\n    bot.chat('Arrived at my goal!')\r\n  }\r\n})\r\n"
  },
  {
    "path": "examples/tutorial/goalComposite.js",
    "content": "/*\r\n * This example shows the usage of the\r\n * GoalCompositeAny and GoalCompositeAll\r\n * goals for mineflayer-pathfinder\r\n *\r\n * See a more detailed explanation here:\r\n * https://github.com/PrismarineJS/mineflayer-pathfinder/blob/master/examples/tutorial/goalsExplained.md\r\n *\r\n * Made by Jovan04 06/07/2023\r\n*/\r\n\r\n// import mineflayer & related libraries\r\nconst mineflayer = require('mineflayer')\r\nconst { pathfinder, Movements, goals: { GoalNear, GoalCompositeAny, GoalCompositeAll } } = require('mineflayer-pathfinder')\r\n\r\n// create mineflayer bot\r\nconst bot = mineflayer.createBot({\r\n  host: 'localhost',\r\n  port: 25565,\r\n  version: '1.18.2',\r\n  auth: 'offline',\r\n  username: 'biffed'\r\n})\r\n\r\n// load pathfinder plugin and set our bot's Movements\r\nbot.once('spawn', () => {\r\n  bot.loadPlugin(pathfinder)\r\n  bot.pathfinder.setMovements(new Movements(bot))\r\n})\r\n\r\nbot.on('chat', async (username, message) => {\r\n  if (username === bot.username) return\r\n\r\n  // create three separate GoalNear goals at different locations, with a range of 5; the bot needs to be within 5 blocks of a given goal to satisfy it\r\n  const LapisGoal = new GoalNear(0, 1, 3, 5)\r\n  const GoldGoal = new GoalNear(3, 1, -2, 5)\r\n  const DiamondGoal = new GoalNear(-3, 1, -2, 5)\r\n\r\n  const goalsArray = [LapisGoal, GoldGoal, DiamondGoal]\r\n\r\n  if (message === 'GoalCompositeAny') {\r\n    bot.chat('Traveling with GoalCompositeAny')\r\n    // create a new GoalCompositeAny: see documentation for a more detailed explanation\r\n    const goalAny = new GoalCompositeAny(goalsArray)\r\n    // and travel to it\r\n    await bot.pathfinder.goto(goalAny)\r\n    bot.chat('Done traveling with GoalCompositeAny')\r\n  }\r\n\r\n  if (message === 'GoalCompositeAll') {\r\n    bot.chat('Traveling with GoalCompositeAll')\r\n    // create a new GoalCompositeAll: see documentation for a more detailed explanation\r\n    const goalAll = new GoalCompositeAll(goalsArray)\r\n    // and travel to it\r\n    await bot.pathfinder.goto(goalAll)\r\n    bot.chat('Done traveling with GoalCompositeAll')\r\n  }\r\n})\r\n"
  },
  {
    "path": "examples/tutorial/goalsExplained.md",
    "content": "<!-- Explanation of how to use goals in mineflayer-pathfinder. Made by Jovan04 06/07/2023 -->\r\n\r\n# Mineflayer-Pathfinder: Goals\r\n## A (more) detailed explanation by Jovan04\r\n\r\nThis page is an explanation about goals in mineflayer-pathfinder. A `Goal` is an instance of a class that allows the user to specify a location that they want a mineflayer bot to go to. Goals are the backbone of mineflayer because they provide an easy way to control your mineflayer bot with mineflayer-pathfinder.  \r\n\r\n### General Goals\r\nIt's useful to think about goals in pathfinder as conditions that need to be fulfilled. For example, one of the most common goals is `GoalBlock`. As the [documentation](../../readme.md#goals) for the `GoalBlock` goal says:\r\n\r\n> One specific block that the player should stand inside at foot level\r\n\r\nThus, we have our condition. In order to complete the `GoalBlock` goal, our bot needs to get its feet inside the specified block. Simple as that.  \r\nThat's great and all, but how do we use it?  \r\n[Here's](./basic.js) a quick example. Let's walk through everything it does.\r\n\r\nFirst, we need to import mineflayer, as well as pathfinder and things related to it:  \r\n```js\r\nconst mineflayer = require('mineflayer')\r\nconst { pathfinder, Movements, goals:{ GoalBlock } } = require('mineflayer-pathfinder')\r\n```\r\nHere, we import a few things from pathfinder:\r\n* the pathfinder plugin itself\r\n* the Movements() class, which defines how our bot is allowed to move\r\n* the `goals` object, and from it the `GoalBlock` goal  \r\n\r\nNext, we'll create our bot. This is just like creating any other Mineflayer bot:  \r\n```js\r\nconst bot = mineflayer.createBot({\r\n  host: 'localhost',\r\n  port: 25565,\r\n  username: 'Pathfinder',\r\n  auth: 'offline'\r\n})\r\n```\r\n\r\nWe'll also add a `spawn` event listener to load the pathfinder plugin and create the bot's Movements class:  \r\n```js\r\nbot.once('spawn', () => {\r\n  bot.loadPlugin(pathfinder) // load pathfinder plugin into the bot\r\n  const defaultMovements = new Movements(bot) // create a new instance of the `Movements` class\r\n  bot.pathfinder.setMovements(defaultMovements) // set the bot's movements to the `Movements` we just created\r\n})\r\n```\r\nThe `Movements` class essentially tells the bot what moves it's allowed to make. This includes, but isn't limited to: What blocks (if any) it can place/break, whether it can pillar straight up, and how far it's allowed to fall.  \r\nWhat we've done so far is mostly boilerplate; you'll probably use it every time you use pathfinder.  \r\n\r\nNow, we'll create a chat listener that we'll use to make pathfinder move:  \r\n```js\r\nbot.on('chat', async (username, message) => {\r\n  if (username === bot.username) return // make bot ignore its own messages\r\n\r\n  if (message === 'go') { // this is our trigger message (only works on servers with vanilla chat)\r\n    // our pathfinder code goes here!\r\n  }\r\n})\r\n```\r\n\r\nNow, let's go back to the `GoalBlock` goal. If we look at the documentation again, we can see that the GoalBlock takes three arguments: an `x` coordinate, a `y` coordinate, and a `z` coordinate, all integers. In order to use the `GoalBlock` goal, we would need to create a new instance of the `GoalBlock` class with those coordinates. If we wanted our bot to pathfind to the coordinates **15, 3, 75**, we could do that like this:\r\n```js\r\nconst myGoal = new GoalBlock(15, 3, 75)\r\n```\r\n\r\nNow that we have our goal, how do we use it? Simple! Pathfinder has a method for traveling to a goal, `goto`. We can access it through `bot.pathfinder.goto(goal)`, and we'll use the `await` Javascript keyword to make our code wait for the bot to finish walking to the goal beore continuing:\r\n```js\r\nawait bot.pathfinder.goto(myGoal)\r\n```\r\n\r\nWe can also use `bot.chat` to have the bot tell us when it starts and finishes going to the goal. If we add that to the lines we just wrote, it can look something like this:\r\n```js\r\nbot.chat('Going to my goal!')\r\nconst myGoal = new GoalBlock(15, 3, 75)\r\nawait bot.pathfinder.goto(myGoal)\r\nbot.chat('Arrived at my goal!')\r\n```\r\n\r\nLet's put those four lines inside our chat listener, likeso:\r\n```js\r\nbot.on('chat', async (username, message) => {\r\n  if (username === bot.username) return // make bot ignore its own messages\r\n\r\n  if (message === 'go') { // this is our trigger message (only works on servers with vanilla chat)\r\n    bot.chat('Going to my goal!')\r\n    const myGoal = new GoalBlock(15, 3, 75)\r\n    await bot.pathfinder.goto(myGoal)\r\n    bot.chat('Arrived at my goal!')\r\n  }\r\n})\r\n```\r\n\r\nAnd now we're done! We can type `go` in chat, and the bot will walk to the coordinates we specified. Remember that `GoalBlock` makes the bot put its feet in the block we specified. You can view and download the full example script [here](./basic.js).\r\n\r\nAnd, there you have it. That's how you use the `GoalBlock` goal! Most of the other goals are used in a similar way, but with different arguments. You can look at [the documentation](../../readme.md#goals) for those. However, there are a few goals that are a little confusing.  \r\n\r\n### Composite Goals\r\nThe Composite goals, `GoalCompositeAny` and `GoalCompositeAll`, are both quite different from most other goals. Instead of being standalone goals themselves, they allow you to combine other goals in interesting ways. They're called *compos*ite goals because they're *compos*ed of (or made up of) other goals. But before we can talk about the composite goals in more detail, we should talk about the `GoalNear` goal. The composite goals are made up of other goals, and `GoalNear` is a good example.  \r\n\r\n`GoalNear` is almost the same as `GoalBlock`, but with one extra argument. Where `GoalBlock` only has `x, y, z`, `GoalNear` has `x, y, z, range`. The additional argument, `range`, specifies how far away from the target block the bot can be in order to still satisfy the goal. For example, the goal `GoalNear(15, 3, 75, 5)` would be satisfied once the bot is within 5 blocks of **15, 3, 75**.  \r\n\r\nNow, let's set up our program to use composite goals.  \r\n\r\nIn the [composite goal example](./goalComposite.js), we make three goals: `LapisGoal`, `GoldGoal`, and `DiamondGoal`. They correspond to standing within 5 blocks of a Lapis block, a Gold block, and a Diamond block, respectively (see picture below):  \r\n```js\r\n  const LapisGoal = new GoalNear(0, 1, 3, 5) // our bot needs to stand within 5 blocks of the point (0, 1, 3) in order to satisfy this goal (blue circle below)\r\n  const GoldGoal = new GoalNear(3, 1, -2, 5) // our bot needs to stand within 5 blocks of the point (3, 1, -2) in order to satisfy this goal (yellow circle below)\r\n  const DiamondGoal = new GoalNear(-3, 1, -2, 5) // our bot needs to stand within 5 blocks of the point (-3, 1, -2) in order to satisfy this goal (white circle below)\r\n```\r\n![Diagram of the three goals](goalComposite-goals.png)\r\n\r\n#### GoalCompositeAny\r\nThe first type of composite goal is called `GoalCompositeAny`. Being a composite goal, `GoalCompositeAny` is a goal made up of other goals. In order for your bot to complete `GoalCompositeAny`, it needs to satisfy *any one* of the contained goals. Above, we made three goals (`LapisGoal`, `GoldGoal`, and `DiamondGoal`).  \r\nLet's put them into an array:  \r\n```js\r\nconst goalsArray = [LapisGoal, GoldGoal, DiamondGoal] // array containing all three of our goals; we'll use this array in our `GoalCompositeAny` goal\r\n```\r\nAnd create a new `GoalCompositeAny` with that array:  \r\n```js\r\nconst goalAny = new GoalCompositeAny(goalsArray)\r\n```\r\nNow, just like any other goal, we can tell pathfinder to `goto` our new `GoalCompositeAny`:  \r\n```js\r\nawait bot.pathfinder.goto(goalAny)\r\n```\r\nThe `GoalCompositeAny` is completed when the bot completes any one of the goals it was created with. For our example above, `goalAny` will be completed when the bot completes any one of `LapisGoal`, `GoldGoal`, or `DiamondGoal`. In other words, `goalAny` will be completed when the bot's location is inside the blue circle *or* the yellow circle *or* the white circle (see image above).  \r\n\r\n#### GoalCompositeAll\r\nThe other type of composite goal is called `GoalCompositeAll`. Being a composite goal, `GoalCompositeAll` is a goal made up of other goals. In order for your bot to complete `GoalCompositeAll`, it needs to satisfy *all* of the contained goals. Above, we made three goals (`LapisGoal`, `GoldGoal`, and `DiamondGoal`).  \r\nLet's put them into an array:  \r\n```js\r\nconst goalsArray = [LapisGoal, GoldGoal, DiamondGoal] // array containing all three of our goals; we'll use this array in our `GoalCompositeAll` goal\r\n```\r\nAnd create a new `GoalCompositeAll` with that array:  \r\n```js\r\nconst goalAll = new GoalCompositeAll(goalsArray)\r\n```\r\nNow, just like any other goal, we can tell pathfinder to `goto` our new `GoalCompositeAll`:  \r\n```js\r\nawait bot.pathfinder.goto(goalAll)\r\n```\r\nThe `GoalCompositeAll` is completed when the bot completes all of the goals it was created with. For our example above, `goalAll` will be completed when the bot completes all of `LapisGoal`, `GoldGoal`, or `DiamondGoal`. In other words, `goalAll` will be completed when the bot's location is inside the blue circle *and* the yellow circle *and* the white circle (see image above). It's important to clarify that the bot needs to be in all three circles at the same time. Going from one circle to another to the last is not a valid path to complete the goal.  \r\n\r\nAnd there you have it! That's a basic introduction to using goals in mineflayer-pathfinder. If any of this was confusing, or you'd like help with something more complicated, feel free to join the [PrismarineJS Discord server](https://discord.gg/GsEFRM8). We're always happy to provide help for mineflayer and other PrismarineJS libraries.\r\n"
  },
  {
    "path": "history.md",
    "content": "# History\n\n# 2.4.5\n* [Fix block update resets for optimized paths (@m000z0rz)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/330)\n* [reword .stop() for more clarity (@Jovan-04)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/327)\n* [add more detailed tutorial for mineflayer-pathfinder goals (@Jovan-04)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/326)\n* [Fixed typo: \"physicsTick\" (@FreezeEngine)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/324)\n\n# 2.4.4\n* [Update readme.md (@Vinciepincie)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/320)\n* Fix file linting (@IceTank)\n* Fix possible reference error for block updates (@IceTank)\n* [Fix a bunch of spelling/grammar errors (@182exe)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/316)\n\n# 2.4.3\n* [Change canOpenDoors default value to false because its buggy (@IceTank)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/318)\n* [Added missing constructor definitions for GoalCompositeAny and GoalCompositeAll (@rutexd)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/315)\n* [Added \"sneak\" on interact blocks to avoid open it (@sefirosweb)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/314)\n* [Block face position fix (@WhoTho)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/312)\n* [Remove mcData param in movements.js (@rtm516)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/311)\n\n# 2.4.2\n* Fix pathfinder trying to make a parkour jump that fails most times\n* Fix pathfinder not going below level 0\n* [Fix wheat not being break able (@maximmasiutin)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/303)\n* [Add parameter typing to Composite goals (@Ic3Tank)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/302)\n* [Fix GoalLookAtBlock documentation (@Ic3Tank)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/301)\n\n# 2.4.1\n* [Made some Goal methods none abstract (@IceTank)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/298)\n\n# 2.4.0\n* [mcData to registry refactoring (@Epirito)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/293)\n\n# 2.3.3\n* [Add missing types on GoalPlaceBlock and GoalLookAtBlock (@IceTank)](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/289)\n\n# 2.3.2\n* Revert broken goto implementation update.\n\n# 2.3.1\n* Fix reference error in `getNumEntitiesAt` (https://github.com/PrismarineJS/mineflayer-pathfinder/commit/2e7b3daff2ee5fa0aaf52db4553f769189b8d03f)\n\n# 2.3.0\n* [Add entity Avoidance Feature](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/281)\n* [Fix bugs in movements.js](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/276)\n* [Update Dependencies](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/277)\n* [Fix issue with starting paths](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/280)\n\n# 2.2.0\n* [Add events typings](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/266)\n* [Force look at blocks when breaking them](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/274)\n* [Fix bot sometimes not triggering path end correctly](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/268)\n* [Fix missing null check at block update](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/269)\n* [Bump mocha to 10.x](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/265)\n\n# 2.1.1\n* Fix GitHub action for publishing\n\n# 2.1.0\n* [Add automated tests](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/262)\n* [Add getPathFromTo function](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/255)\n* [Fix path optimization check](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/254)\n* [Bumb minecraft data to version 3.x](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/262)\n* [Add goal chaining example](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/256)\n\n# 2.0.0\n* [Remove callbacks](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/248)\n* [Export GoalLookAtBlock and deprecate GoalBreakBlock](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/249)\n\n# 1.10.0\n\n* [Add exclusion area mechanism](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/220)\n* [Add movement class example](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/247)\n* [Add infiniteLiquidDropdownDistance to movements](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/211)\n* [Added dontMineUnderFallingBlock to movements](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/239/files)\n* [Add ability to open fence gates](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/217)\n\n* [Bump mineflayer to 4.0.0](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/245)\n* [Throw error in goto when stop() is called](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/240)\n* [Update README.md](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/246)\n\n* Typing fixes:\n  * [tickTimeout](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/241)\n  * [GoalLookAtBlock](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/238)\n\n* [Fix dynamic goals with entities](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/206)\n* [Fix default scaffolding blocks](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/243)\n* [Fix event handler when stop() is called](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/184)\n\n# 1.9.1\n\n* [Fixed unhandled promise rejection introduced in 1.0.0](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/235#event-5854609665)\n\n# 1.9.0\n\n* [Fixed floor check](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/208)\n* [Avoid cobwebs](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/210)\n* [Fixed diagonal move not considering collision height when jumping up diagonally](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/216)\n* [Fixed movements for older versions](https://github.com/PrismarineJS/mineflayer-pathfinder/pull/226)\n\n# 1.8.0\n\n* Fixed placeBlock example\n* Fixed Readme typos\n* Fixed bot placing wrong blocks as scaffolding\n* Fixed GoalNearXZ\n* Fixed typings\n* Fixed index.d.ts compile errors\n\n# 1.7.0\n\n* Add GoalNearXZ\n* Improve docs\n\n# 1.6.3\n\n* Add setGoal null to typings\n* Add safeOrBreak check to getMoveDiagonal\n* Fix reference to LOSWhenPlacingBlocks\n* Fixed raycasting not considering block face\n* Add GoalPlaceBlock typing\n* Add placeBlock.js example\n* Add callback.js and promise.js example\n* Fix reference error in GoalPlaceBlock\n* Function to stop path finding when safe\n\n# 1.6.2\n\n* Fix swimming in lava\n* Fix TypeScript headers\n* Add +1 to movement cost when going forward and up 1 block\n* Fix bot trying to go underwater\n* Add `path_reset` event\n\n# 1.6.1\n\n* Add option to limit search range\n* Expose tickTimeout\n\n# 1.6.0\n\n* Add GoalPlaceBlock\n* Fix various parkour moves\n* Fix goto\n\n# 1.5.0\n\n* Improve diagonal movements (add up/down)\n* Expose A* context in result\n* Fix fences\n* Fix carpets\n\n# 1.4.0\n\n* Legit bot bridging (with sneak)\n* Fixed bug for detect when mining is stopped correctly\n* Fix GoalGetToBlock\n\n## 1.3.7\n\n* Promisified goto\n\n## 1.3.0\n\n* Add ladder support\n* Add ability to drop in water from high places\n* Improve movement execution\n\n## 1.2.0\n\n* Use physics to predict motion and choose best controls\n* Add more parkour moves\n* Sprint and sprint-jump over gaps\n\n## 1.1.2\n\n* Set every non diggable block automatically from mcdata\n* Fix jumps in snow\n\n## 1.1.1\n\n* Fix 1x1 towering\n* Fix path starting on shorter blocks\n\n## 1.1.0\n\n* Fixed crash with null positions\n* API in the readme\n* Expose movements and goal\n\n## 1.0.12\n\n* Added `canDig` movements variable\n* Added `goto(goal, cb)` function\n\n## 1.0.11\n\n* Added `goal_updated` event\n* Movements are now initialized by default\n* Fixed Typescript headers\n* Fixed bugs with block height when jumping\n\n## 1.0.10\n\n* Fixed \"cannot read property 'shapes' of null\" bug\n* Exposed `thinkTimeout` pathfinder variable\n\n## 1.0.9\n\n* Added simple postprocessing fallback for unsuitable positions\n\n## 1.0.8\n\n* Fixed null pointer exception for \"getPositionOnTopOf\"\n\n## 1.0.7\n\n* Improved post processing for standing on more block types\n* Improved tool selection when breaking blocks\n* Retrieve player state from Prismarine-Physics\n* Fixed bug with parkour cooldown\n* Added Typescript headers\n* Fixed bug with clearing controls while recalculating path\n* Removed path recalculation detection radius\n\n## 1.0.6\n\n* Added basic parkour movements\n* Movement nodes are now stored as classes\n* Astar algorithm is now stored as a class\n* Improved blocks-to-break estimation in the path\n* Fixed 12.x Node.js compatibility in example bot\n\n## 1.0.5\n\n* Added multiple bot example\n* Added experimental \"free-motion\"\n* Added composite goal\n* Added `isMoving()` function\n* Added `isMining()` function\n* Added `isBuilding()` function\n* Added `isThinking()` function\n\n## 1.0.4\n\n* Paths are now recalculated on chunk loading to fix long paths\n* Minor bug fixes\n* Moved scaffolding blocks from index.js to movements.js internally\n* Updated readme todo list\n* Added 1x1 tower creation\n\n## 1.0.3\n\n* Fixed `goal_reached` not being called if bot is already at the goal\n* Control state is cleared when path is reset\n* Fixed example bot code in readme\n* Improved readme\n* Fixed bug with place/dig logic\n* Added swimming support\n\n## 1.0.2\n\n* Exposed goals and movements classes\n* Fixed bugs with bot stopping incorrectly\n* Improved readme\n* Added performance benchmarks\n* Added build CI support\n\n## 1.0.1\n\n* Added deployment CI support\n* Added standard\n* Fixed bug with not canceling digging when resetting path\n* Fixed undefined pos error\n* Added configurable fall height\n* Added dynamic goals\n* Added automatic path recalculation\n* Added 1x1 digging holes\n* Added more movement abilities\n* Added internal scaffolding block count\n\n## 1.0.0\n\n* Initial release\n"
  },
  {
    "path": "index.d.ts",
    "content": "import { Bot } from 'mineflayer';\nimport { IndexedData } from 'minecraft-data';\nimport { Item } from 'prismarine-item';\nimport { Vec3 } from 'vec3';\nimport { Block } from 'prismarine-block';\nimport { Entity } from 'prismarine-entity';\nimport { World } from 'prismarine-world'\nimport AStar from './lib/astar';\n\ndeclare module 'mineflayer-pathfinder' {\n\texport function pathfinder(bot: Bot): void;\n\n\texport interface Pathfinder {\n\t\tthinkTimeout: number;\n\t\t/** ms, amount of thinking per tick (max 50 ms) */\n\t\ttickTimeout: number;\n\t\treadonly goal: goals.Goal | null;\n\t\treadonly movements: Movements;\n\n\t\tbestHarvestTool(block: Block): Item | null;\n\t\tgetPathTo(\n\t\t\tmovements: Movements,\n\t\t\tgoal: goals.Goal,\n\t\t\ttimeout?: number\n\t\t): ComputedPath;\n\t\tgetPathFromTo(\n\t\t\tmovements: Movements,\n\t\t\tstartPos: Vec3 | null, \n\t\t\tgoal: goals.Goal, \n\t\t\toptions?: {\n\t\t\t\toptimizePath?: boolean,\n\t\t\t\tresetEntityIntersects?: boolean,\n\t\t\t\ttimeout?: number,\n\t\t\t\ttickTimeout?: number,\n\t\t\t\tsearchRadius?: number,\n\t\t\t\tstartMove?: Move\n\t\t\t}\n\t\t): IterableIterator<{ result: ComputedPath, astarContext: AStar }>\n\n\t\tsetGoal(goal: goals.Goal | null, dynamic?: boolean): void;\n\t\tsetMovements(movements: Movements): void;\n\t\tgoto(goal: goals.Goal, callback?: Callback): Promise<void>;\n\t\tstop(): void;\n\n\t\tisMoving(): boolean;\n\t\tisMining(): boolean;\n\t\tisBuilding(): boolean;\n\t}\n\n\texport namespace goals {\n\t\texport abstract class Goal {\n\t\t\tpublic abstract heuristic(node: Move): number;\n\t\t\tpublic abstract isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t\tpublic isValid(): boolean;\n\t\t}\n\n\t\texport class GoalBlock extends Goal {\n\t\t\tpublic constructor(x: number, y: number, z: number);\n\n\t\t\tpublic x: number;\n\t\t\tpublic y: number;\n\t\t\tpublic z: number;\n\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t}\n\n\t\texport class GoalNear extends Goal {\n\t\t\tpublic constructor(x: number, y: number, z: number, range: number);\n\n\t\t\tpublic x: number;\n\t\t\tpublic y: number;\n\t\t\tpublic z: number;\n\t\t\tpublic rangeSq: number;\n\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t}\n\n\t\texport class GoalXZ extends Goal {\n\t\t\tpublic constructor(x: number, z: number);\n\n\t\t\tpublic x: number;\n\t\t\tpublic z: number;\n\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t}\n\n\t\texport class GoalNearXZ extends Goal {\n\t\t\tpublic constructor(x: number, z: number, range: number);\n\n\t\t\tpublic x: number;\n\t\t\tpublic z: number;\n\t\t\tpublic rangeSq: number;\n\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t}\n\n\t\texport class GoalY extends Goal {\n\t\t\tpublic constructor(y: number);\n\n\t\t\tpublic y: number;\n\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t}\n\n\t\texport class GoalGetToBlock extends Goal {\n\t\t\tpublic constructor(x: number, y: number, z: number);\n\n\t\t\tpublic x: number;\n\t\t\tpublic y: number;\n\t\t\tpublic z: number;\n\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t}\n\n\t\texport class GoalCompositeAny<T extends Goal> extends Goal {\n\t\t\tpublic constructor(goals: T[] = []);\n\t\t\tpublic goals: T[];\n\t\t\t\n\t\t\tpublic push(goal: Goal): void;\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t}\n\n\t\texport class GoalCompositeAll<T extends Goal> extends Goal {\n\t\t\tpublic constructor(goals: T[] = []);\n\t\t\tpublic goals: T[];\n\n\t\t\tpublic push(goal: Goal): void;\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t}\n\n\t\texport class GoalInvert extends Goal {\n\t\t\tpublic constructor(goal: Goal);\n\t\t\t\n\t\t\tpublic goal: Goal;\n\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t}\n\n\t\texport class GoalFollow extends Goal {\n\t\t\tpublic constructor(entity: Entity, range: number);\n\n\t\t\tpublic x: number;\n\t\t\tpublic y: number;\n\t\t\tpublic z: number;\n\t\t\tpublic entity: Entity;\n\t\t\tpublic rangeSq: number;\n\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t}\n\n\t\texport class GoalPlaceBlock extends Goal {\n\t\t\tpublic options: {\n\t\t\t\trange: number;\n\t\t\t\tLOS: boolean;\n\t\t\t\tfaces: [Vec3, Vec3, Vec3, Vec3, Vec3, Vec3];\n\t\t\t\tfacing: number;\n\t\t\t\thalf: boolean;\n\t\t\t}\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t\tpublic constructor(pos: Vec3, world: World, options: GoalPlaceBlockOptions)\n\t\t}\n\t\t\n\t\texport class GoalLookAtBlock  extends Goal {\n\t\t\tpublic constructor(pos: Vec3, world: World, options?: { reach?: number, entityHeight?: number })\n\t\t\t\n\t\t\tpublic pos: Vec3;\n\t\t\tpublic reach: number;\n\t\t\tpublic entityHeight: number;\n\t\t\tpublic world: World;\n\n\t\t\tpublic heuristic(node: Move): number;\n\t\t\tpublic isEnd(node: Move): boolean;\n\t\t\tpublic hasChanged(): boolean;\n\t\t}\n\n\t\texport class GoalBreakBlock extends GoalLookAtBlock {}\n\t}\n\n\texport class Movements {\n\t\tpublic constructor(bot: Bot);\n\n\t\tpublic bot: Bot;\n\n\t\tpublic canDig: boolean;\n\t\tpublic canOpenDoors: boolean;\n\t\tpublic dontCreateFlow: boolean;\n\t\tpublic dontMineUnderFallingBlock: boolean;\n\t\tpublic allow1by1towers: boolean;\n\t\tpublic allowFreeMotion: boolean;\n\t\tpublic allowParkour: boolean;\n\t\tpublic allowSprinting: boolean;\n \t\t/**\n \t\t * Test for entities that may obstruct path or prevent block placement. Grabs updated entities every new path\n \t\t */\n \t\tpublic allowEntityDetection: boolean;\n\t\t\n\t\t/**\n\t\t * Set of entities (by mcdata name) to completely avoid when using entity detection\n\t\t */\n\t\tpublic entitiesToAvoid: Set<string>;\n\t\t/**\n\t\t * Set of entities (by mcdata name) to ignore when using entity detection\n\t\t */\n\t\tpublic passableEntities: Set<string>;\n\t\t/**\n\t\t * Set of blocks (by mcdata name) that pathfinder should not attempt to place blocks or 'right click' on\n\t\t */\n\t\tpublic interactableBlocks: Set<string>;\n\t\tpublic blocksCantBreak: Set<number>;\n\t\tpublic blocksToAvoid: Set<number>;\n\t\tpublic liquids: Set<number>;\n\t\tpublic gravityBlocks: Set<number>;\n\t\tpublic climbables: Set<number>\n\t\tpublic emptyBlocks: Set<number>\n\t\tpublic replaceables: Set<number>\n\t\tpublic fences: Set<number>\n\t\tpublic carpets: Set<number>\n\t\tpublic openable: Set<number>\n\n\t\tpublic scafoldingBlocks: number[];\n\n\t\tpublic maxDropDown: number;\n\t\tpublic infiniteLiquidDropdownDistance: boolean;\n\t\tpublic digCost: number;\n\t\tpublic placeCost: number;\n \t\t/**\n \t\t * Extra cost multiplier for moving through an entity hitbox (besides passable ones).\n \t\t */\n \t\tpublic entityCost: number;\n\n\t\t/** Exclusion Area that adds extra cost or prevents the bot from stepping onto positions included.\n\t\t * @example\n\t\t * ```js\n\t\t\tmovements.exclusionAreas = [(block) => {\n\t\t\t\treturn block.type === someIdType ? 100 : 0 // Prevents the bot from breaking a specific block. By adding 100 to the cost.\n\t\t\t},\n\t\t\t(block) => {\n\t\t\t\treturn someVec3Pos.distanceTo(block.position) < 5 ? 100 : 0 // Prevents the bot from getting near to a specific location\n\t\t\t}]\n\t\t\t``` */\n\t\tpublic exclusionAreasStep: [(block: SafeBlock) => number];\n\t\t/**\n\t\t * Exclusion area for blocks to break. Works in the same way as {@link exclusionAreasStep} does. \n\t\t */\n\t\tpublic exclusionAreasBreak: [(block: SafeBlock) => number];\n\t\t/**\n\t\t * Exclusion area for placing blocks. Note only works for positions not block values as placed blocks are determined by the bots inventory content. Works in the same way as {@link exclusionAreasStep} does. \n\t\t */\n\t\tpublic exclusionAreasPlace: [(block: SafeBlock) => number];\n        \n \t\t/**\n \t\t * A dictionary of the number of entities intersecting each floored block coordinate.\n \t\t * Updated automatically each path but, you may mix in your own entries before calculating a path if desired (generally for testing).\n \t\t * To prevent this from being cleared automatically before generating a path see getPathFromTo()\n \t\t * formatted entityIntersections['x,y,z'] = #ents\n \t\t */\n\t\tpublic entityIntersections: {string: number};\n\n\t\tpublic exclusionPlace(block: SafeBlock): number;\n\t\tpublic exclusionStep(block: SafeBlock): number;\n\t\tpublic exclusionBreak(block: SafeBlock): number;\n\t\tpublic countScaffoldingItems(): number;\n\t\tpublic getScaffoldingItem(): Item | null;\n\t\tpublic clearCollisionIndex(): void;\n\t\t/**\n\t\t * Finds blocks intersected by entity bounding boxes\n\t\t * and sets the number of ents intersecting in a dict.\n\t\t * Ignores entities that do not affect block placement\n\t\t */\n\t\tpublic updateCollisionIndex(): void;\n\t\t/**\n\t\t * Gets number of entities who's bounding box intersects the node + offset\n\t\t * @param {import('vec3').Vec3} pos node position\n\t\t * @param {number} dx X axis offset\n\t\t * @param {number} dy Y axis offset\n\t\t * @param {number} dz Z axis offset\n\t\t * @returns {number} Number of entities intersecting block\n\t\t */\n\t\tpublic getNumEntitiesAt(pos: Vec3, dx: number, dy: number, dz: number): number;\n\t\tpublic getBlock(pos: Vec3, dx: number, dy: number, dz: number): SafeBlock;\n\t\tpublic safeToBreak(block: SafeBlock): boolean;\n\t\tpublic safeOrBreak(block: SafeBlock): number;\n\t\tpublic getMoveJumpUp(node: Move, dir: XZCoordinates, neighbors: Move[]): void;\n\t\tpublic getMoveForward(node: Move, dir: XZCoordinates, neighbors: Move[]): void;\n\t\tpublic getMoveDiagonal(node: Move, dir: XZCoordinates, neighbors: Move[]): void;\n\t\tpublic getMoveDropDown(node: Move, dir: XZCoordinates, neighbors: Move[]): void;\n\t\tpublic getMoveParkourForward(node: Move, dir: XZCoordinates, neighbors: Move[]): void;\n\t\tpublic getMoveJumpUp(node: Move, dir: XZCoordinates, neighbors: Move[]): void;\n\t\tpublic getMoveUp(node: Move, neighbors: Move[]): void;\n\t\tpublic getMoveDown(node: Move, neighbors: Move[]): void;\n\t\tpublic getLandingBlock(node: Move, dir: XZCoordinates): SafeBlock;\n\t\tpublic getNeighbors(node: Move): Move[];\n\t}\n\n\t// this is a class, but its not exported so we use an interface\n\texport interface Move extends XYZCoordinates {\n\t\tremainingBlocks: number;\n\t\tcost: number;\n\t\ttoBreak: Move[];\n\t\ttoPlace: Move[];\n\t\tparkour: boolean;\n\t\thash: string;\n\t}\n\n\ttype Callback = (error?: Error) => void;\n\n\tinterface PathBase {\n\t\tcost: number;\n\t\ttime: number;\n\t\tvisitedNodes: number;\n\t\tgeneratedNodes: number;\n\t\tpath: Move[];\n\t}\n\n\texport interface ComputedPath extends PathBase {\n\t\tstatus: 'noPath' | 'timeout' | 'success';\n\t}\n\n\texport interface PartiallyComputedPath extends PathBase {\n\t\tstatus: 'noPath' | 'timeout' | 'success' | 'partial';\n\t}\n\n\texport interface XZCoordinates {\n\t\tx: number;\n\t\tz: number;\n\t}\n\n\texport interface XYZCoordinates extends XZCoordinates {\n\t\ty: number;\n\t}\n\n\texport interface SafeBlock extends Block {\n\t\tsafe: boolean;\n\t\tphysical: boolean;\n\t\tliquid: boolean;\n\t\theight: number;\n\t\treplaceable: boolean;\n\t\tclimbable: boolean;\n\t\topenable: boolean;\n\t}\n\n\texport interface GoalPlaceBlockOptions {\n\t\trange: number;\n\t\tLOS: boolean;\n\t\tfaces: Vec3[];\n\t\tfacing: 'north' | 'east' | 'south' | 'west' | 'up' | 'down';\n\t}\n}\n\ndeclare module 'mineflayer' {\n\tinterface BotEvents {\n\t\tgoal_reached: (goal: Goal) => void;\n\t\tpath_update: (path: PartiallyComputedPath) => void;\n\t\tgoal_updated: (goal: Goal, dynamic: boolean) => void;\n\t\tpath_reset: (\n\t\t\treason: 'goal_updated' | 'movements_updated' |\n\t\t\t\t'block_updated' | 'chunk_loaded' | 'goal_moved' | 'dig_error' |\n\t\t\t\t'no_scaffolding_blocks' | 'place_error' | 'stuck'\n\t\t) => void;\n\t\tpath_stop: () => void;\n\t}\n\n\tinterface Bot {\n\t\tpathfinder: Pathfinder\n\t}\n}\n"
  },
  {
    "path": "index.js",
    "content": "const { performance } = require('perf_hooks')\n\nconst AStar = require('./lib/astar')\nconst Move = require('./lib/move')\nconst Movements = require('./lib/movements')\nconst gotoUtil = require('./lib/goto')\nconst Lock = require('./lib/lock')\n\nconst Vec3 = require('vec3').Vec3\n\nconst Physics = require('./lib/physics')\nconst nbt = require('prismarine-nbt')\nconst interactableBlocks = require('./lib/interactable.json')\n\nfunction inject (bot) {\n  const waterType = bot.registry.blocksByName.water.id\n  const ladderId = bot.registry.blocksByName.ladder.id\n  const vineId = bot.registry.blocksByName.vine.id\n  let stateMovements = new Movements(bot)\n  let stateGoal = null\n  let astarContext = null\n  let astartTimedout = false\n  let dynamicGoal = false\n  let path = []\n  let pathUpdated = false\n  let digging = false\n  let placing = false\n  let placingBlock = null\n  let lastNodeTime = performance.now()\n  let returningPos = null\n  let stopPathing = false\n  const physics = new Physics(bot)\n  const lockPlaceBlock = new Lock()\n  const lockEquipItem = new Lock()\n  const lockUseBlock = new Lock()\n\n  bot.pathfinder = {}\n\n  bot.pathfinder.thinkTimeout = 5000 // ms\n  bot.pathfinder.tickTimeout = 40 // ms, amount of thinking per tick (max 50 ms)\n  bot.pathfinder.searchRadius = -1 // in blocks, limits of the search area, -1: don't limit the search\n  bot.pathfinder.enablePathShortcut = false // disabled by default as it can cause bugs in specific configurations\n  bot.pathfinder.LOSWhenPlacingBlocks = true\n\n  bot.pathfinder.bestHarvestTool = (block) => {\n    const availableTools = bot.inventory.items()\n    const effects = bot.entity.effects\n\n    let fastest = Number.MAX_VALUE\n    let bestTool = null\n    for (const tool of availableTools) {\n      const enchants = (tool && tool.nbt) ? nbt.simplify(tool.nbt).Enchantments : []\n      const digTime = block.digTime(tool ? tool.type : null, false, false, false, enchants, effects)\n      if (digTime < fastest) {\n        fastest = digTime\n        bestTool = tool\n      }\n    }\n\n    return bestTool\n  }\n\n  bot.pathfinder.getPathTo = (movements, goal, timeout) => {\n    const generator = bot.pathfinder.getPathFromTo(movements, bot.entity.position, goal, { timeout })\n    const { value: { result, astarContext: context } } = generator.next()\n    astarContext = context\n    return result\n  }\n\n  bot.pathfinder.getPathFromTo = function * (movements, startPos, goal, options = {}) {\n    const optimizePath = options.optimizePath ?? true\n    const resetEntityIntersects = options.resetEntityIntersects ?? true\n    const timeout = options.timeout ?? bot.pathfinder.thinkTimeout\n    const tickTimeout = options.tickTimeout ?? bot.pathfinder.tickTimeout\n    const searchRadius = options.searchRadius ?? bot.pathfinder.searchRadius\n    let start\n    if (options.startMove) {\n      start = options.startMove\n    } else {\n      const p = startPos.floored()\n      const dy = startPos.y - p.y\n      const b = bot.blockAt(p) // The block we are standing in\n      // Offset the floored bot position by one if we are standing on a block that has not the full height but is solid\n      const offset = (b && dy > 0.001 && bot.entity.onGround && !stateMovements.emptyBlocks.has(b.type)) ? 1 : 0\n      start = new Move(p.x, p.y + offset, p.z, movements.countScaffoldingItems(), 0)\n    }\n    if (movements.allowEntityDetection) {\n      if (resetEntityIntersects) {\n        movements.clearCollisionIndex()\n      }\n      movements.updateCollisionIndex()\n    }\n    const astarContext = new AStar(start, movements, goal, timeout, tickTimeout, searchRadius)\n    let result = astarContext.compute()\n    if (optimizePath) result.path = postProcessPath(result.path)\n    yield { result, astarContext }\n    while (result.status === 'partial') {\n      result = astarContext.compute()\n      if (optimizePath) result.path = postProcessPath(result.path)\n      yield { result, astarContext }\n    }\n  }\n\n  Object.defineProperties(bot.pathfinder, {\n    goal: {\n      get () {\n        return stateGoal\n      }\n    },\n    movements: {\n      get () {\n        return stateMovements\n      }\n    }\n  })\n\n  function detectDiggingStopped () {\n    digging = false\n    bot.removeAllListeners('diggingAborted', detectDiggingStopped)\n    bot.removeAllListeners('diggingCompleted', detectDiggingStopped)\n  }\n\n  function resetPath (reason, clearStates = true) {\n    if (!stopPathing && path.length > 0) bot.emit('path_reset', reason)\n    path = []\n    if (digging) {\n      bot.on('diggingAborted', detectDiggingStopped)\n      bot.on('diggingCompleted', detectDiggingStopped)\n      bot.stopDigging()\n    }\n    placing = false\n    pathUpdated = false\n    astarContext = null\n    lockEquipItem.release()\n    lockPlaceBlock.release()\n    lockUseBlock.release()\n    stateMovements.clearCollisionIndex()\n    if (clearStates) bot.clearControlStates()\n    if (stopPathing) return stop()\n  }\n\n  bot.pathfinder.setGoal = (goal, dynamic = false) => {\n    stateGoal = goal\n    dynamicGoal = dynamic\n    bot.emit('goal_updated', goal, dynamic)\n    resetPath('goal_updated')\n  }\n\n  bot.pathfinder.setMovements = (movements) => {\n    stateMovements = movements\n    resetPath('movements_updated')\n  }\n\n  bot.pathfinder.isMoving = () => path.length > 0\n  bot.pathfinder.isMining = () => digging\n  bot.pathfinder.isBuilding = () => placing\n\n  bot.pathfinder.goto = (goal) => {\n    return gotoUtil(bot, goal)\n  }\n\n  bot.pathfinder.stop = () => {\n    stopPathing = true\n  }\n\n  bot.on('physicsTick', monitorMovement)\n\n  function postProcessPath (path) {\n    for (let i = 0; i < path.length; i++) {\n      const curPoint = path[i]\n      if (curPoint.toBreak.length > 0 || curPoint.toPlace.length > 0) break\n      const b = bot.blockAt(new Vec3(curPoint.x, curPoint.y, curPoint.z))\n      if (b && (b.type === waterType || ((b.type === ladderId || b.type === vineId) && i + 1 < path.length && path[i + 1].y < curPoint.y))) {\n        curPoint.x = Math.floor(curPoint.x) + 0.5\n        curPoint.y = Math.floor(curPoint.y)\n        curPoint.z = Math.floor(curPoint.z) + 0.5\n        continue\n      }\n      let np = getPositionOnTopOf(b)\n      if (np === null) np = getPositionOnTopOf(bot.blockAt(new Vec3(curPoint.x, curPoint.y - 1, curPoint.z)))\n      if (np) {\n        curPoint.x = np.x\n        curPoint.y = np.y\n        curPoint.z = np.z\n      } else {\n        curPoint.x = Math.floor(curPoint.x) + 0.5\n        curPoint.y = curPoint.y - 1\n        curPoint.z = Math.floor(curPoint.z) + 0.5\n      }\n    }\n\n    if (!bot.pathfinder.enablePathShortcut || stateMovements.exclusionAreasStep.length !== 0 || path.length === 0) return path\n\n    const newPath = []\n    let lastNode = bot.entity.position\n    for (let i = 1; i < path.length; i++) {\n      const node = path[i]\n      if (Math.abs(node.y - lastNode.y) > 0.5 || node.toBreak.length > 0 || node.toPlace.length > 0 || !physics.canStraightLineBetween(lastNode, node)) {\n        newPath.push(path[i - 1])\n        lastNode = path[i - 1]\n      }\n    }\n    newPath.push(path[path.length - 1])\n    return newPath\n  }\n\n  function pathFromPlayer (path) {\n    if (path.length === 0) return\n    let minI = 0\n    let minDistance = 1000\n    for (let i = 0; i < path.length; i++) {\n      const node = path[i]\n      if (node.toBreak.length !== 0 || node.toPlace.length !== 0) break\n      const dist = bot.entity.position.distanceSquared(node)\n      if (dist < minDistance) {\n        minDistance = dist\n        minI = i\n      }\n    }\n    // check if we are between 2 nodes\n    const n1 = path[minI]\n    // check if node already reached\n    const dx = n1.x - bot.entity.position.x\n    const dy = n1.y - bot.entity.position.y\n    const dz = n1.z - bot.entity.position.z\n    const reached = Math.abs(dx) <= 0.35 && Math.abs(dz) <= 0.35 && Math.abs(dy) < 1\n    if (minI + 1 < path.length && n1.toBreak.length === 0 && n1.toPlace.length === 0) {\n      const n2 = path[minI + 1]\n      const d2 = bot.entity.position.distanceSquared(n2)\n      const d12 = n1.distanceSquared(n2)\n      minI += d12 > d2 || reached ? 1 : 0\n    }\n\n    path.splice(0, minI)\n  }\n\n  function isPositionNearPath (pos, path) {\n    let prevNode = null\n    for (const node of path) {\n      let comparisonPoint = null\n      if (\n        prevNode === null ||\n        (\n          Math.abs(prevNode.x - node.x) <= 2 &&\n          Math.abs(prevNode.y - node.y) <= 2 &&\n          Math.abs(prevNode.z - node.z) <= 2\n        )\n      ) {\n        // Unoptimized path, or close enough to last point\n        // to just check against the current point\n        comparisonPoint = node\n      } else {\n        // Optimized path - the points are far enough apart\n        //   that we need to check the space between them too\n\n        // First, a quick check - if point it outside the path\n        // segment's AABB, then it isn't near.\n        const minBound = prevNode.min(node)\n        const maxBound = prevNode.max(node)\n        if (\n          pos.x - 0.5 < minBound.x - 1 ||\n          pos.x - 0.5 > maxBound.x + 1 ||\n          pos.y - 0.5 < minBound.y - 2 ||\n          pos.y - 0.5 > maxBound.y + 2 ||\n          pos.z - 0.5 < minBound.z - 1 ||\n          pos.z - 0.5 > maxBound.z + 1\n        ) {\n          continue\n        }\n\n        comparisonPoint = closestPointOnLineSegment(pos, prevNode, node)\n      }\n\n      const dx = Math.abs(comparisonPoint.x - pos.x - 0.5)\n      const dy = Math.abs(comparisonPoint.y - pos.y - 0.5)\n      const dz = Math.abs(comparisonPoint.z - pos.z - 0.5)\n      if (dx <= 1 && dy <= 2 && dz <= 1) return true\n\n      prevNode = node\n    }\n\n    return false\n  }\n\n  function closestPointOnLineSegment (point, segmentStart, segmentEnd) {\n    const segmentLength = segmentEnd.minus(segmentStart).norm()\n\n    if (segmentLength === 0) {\n      return segmentStart\n    }\n\n    // t is like an interpolation from segmentStart to segmentEnd\n    //  for the closest point on the line\n    let t = (point.minus(segmentStart)).dot(segmentEnd.minus(segmentStart)) / segmentLength\n\n    // bound t to be on the segment\n    t = Math.max(0, Math.min(1, t))\n\n    return segmentStart.plus(segmentEnd.minus(segmentStart).scaled(t))\n  }\n\n  // Return the average x/z position of the highest standing positions\n  // in the block.\n  function getPositionOnTopOf (block) {\n    if (!block || block.shapes.length === 0) return null\n    const p = new Vec3(0.5, 0, 0.5)\n    let n = 1\n    for (const shape of block.shapes) {\n      const h = shape[4]\n      if (h === p.y) {\n        p.x += (shape[0] + shape[3]) / 2\n        p.z += (shape[2] + shape[5]) / 2\n        n++\n      } else if (h > p.y) {\n        n = 2\n        p.x = 0.5 + (shape[0] + shape[3]) / 2\n        p.y = h\n        p.z = 0.5 + (shape[2] + shape[5]) / 2\n      }\n    }\n    p.x /= n\n    p.z /= n\n    return block.position.plus(p)\n  }\n\n  /**\n   * Stop the bot's movement and recenter to the center off the block when the bot's hitbox is partially beyond the\n   * current blocks dimensions.\n   */\n  function fullStop () {\n    bot.clearControlStates()\n\n    // Force horizontal velocity to 0 (otherwise inertia can move us too far)\n    // Kind of cheaty, but the server will not tell the difference\n    bot.entity.velocity.x = 0\n    bot.entity.velocity.z = 0\n\n    const blockX = Math.floor(bot.entity.position.x) + 0.5\n    const blockZ = Math.floor(bot.entity.position.z) + 0.5\n\n    // Make sure our bounding box don't collide with neighboring blocks\n    // otherwise recenter the position\n    if (Math.abs(bot.entity.position.x - blockX) > 0.2) { bot.entity.position.x = blockX }\n    if (Math.abs(bot.entity.position.z - blockZ) > 0.2) { bot.entity.position.z = blockZ }\n  }\n\n  function moveToEdge (refBlock, edge) {\n    // If allowed turn instantly should maybe be a bot option\n    const allowInstantTurn = false\n    function getViewVector (pitch, yaw) {\n      const csPitch = Math.cos(pitch)\n      const snPitch = Math.sin(pitch)\n      const csYaw = Math.cos(yaw)\n      const snYaw = Math.sin(yaw)\n      return new Vec3(-snYaw * csPitch, snPitch, -csYaw * csPitch)\n    }\n    // Target viewing direction while approaching edge\n    // The Bot approaches the edge while looking in the opposite direction from where it needs to go\n    // The target Pitch angle is roughly the angle the bot has to look down for when it is in the position\n    // to place the next block\n    const targetBlockPos = refBlock.offset(edge.x + 0.5, edge.y, edge.z + 0.5)\n    const targetPosDelta = bot.entity.position.clone().subtract(targetBlockPos)\n    const targetYaw = Math.atan2(-targetPosDelta.x, -targetPosDelta.z)\n    const targetPitch = -1.421\n    const viewVector = getViewVector(targetPitch, targetYaw)\n    // While the bot is not in the right position rotate the view and press back while crouching\n    if (bot.entity.position.distanceTo(refBlock.clone().offset(edge.x + 0.5, 1, edge.z + 0.5)) > 0.4) {\n      bot.lookAt(bot.entity.position.offset(viewVector.x, viewVector.y, viewVector.z), allowInstantTurn)\n      bot.setControlState('sneak', true)\n      bot.setControlState('back', true)\n      return false\n    }\n    bot.setControlState('back', false)\n    return true\n  }\n\n  function moveToBlock (pos) {\n    // minDistanceSq = Min distance sqrt to the target pos were the bot is centered enough to place blocks around him\n    const minDistanceSq = 0.2 * 0.2\n    const targetPos = pos.clone().offset(0.5, 0, 0.5)\n    if (bot.entity.position.distanceSquared(targetPos) > minDistanceSq) {\n      bot.lookAt(targetPos)\n      bot.setControlState('forward', true)\n      return false\n    }\n    bot.setControlState('forward', false)\n    return true\n  }\n\n  function stop () {\n    stopPathing = false\n    stateGoal = null\n    path = []\n    bot.emit('path_stop')\n    fullStop()\n  }\n\n  bot.on('blockUpdate', (oldBlock, newBlock) => {\n    if (!oldBlock || !newBlock) return\n    if (isPositionNearPath(oldBlock.position, path) && oldBlock.type !== newBlock.type) {\n      resetPath('block_updated', false)\n    }\n  })\n\n  bot.on('chunkColumnLoad', (chunk) => {\n    // Reset only if the new chunk is adjacent to a visited chunk\n    if (astarContext) {\n      const cx = chunk.x >> 4\n      const cz = chunk.z >> 4\n      if (astarContext.visitedChunks.has(`${cx - 1},${cz}`) ||\n          astarContext.visitedChunks.has(`${cx},${cz - 1}`) ||\n          astarContext.visitedChunks.has(`${cx + 1},${cz}`) ||\n          astarContext.visitedChunks.has(`${cx},${cz + 1}`)) {\n        resetPath('chunk_loaded', false)\n      }\n    }\n  })\n\n  function monitorMovement () {\n    // Test freemotion\n    if (stateMovements && stateMovements.allowFreeMotion && stateGoal && stateGoal.entity) {\n      const target = stateGoal.entity\n      if (physics.canStraightLine([target.position])) {\n        bot.lookAt(target.position.offset(0, 1.6, 0))\n\n        if (target.position.distanceSquared(bot.entity.position) > stateGoal.rangeSq) {\n          bot.setControlState('forward', true)\n        } else {\n          bot.clearControlStates()\n        }\n        return\n      }\n    }\n    if (stateGoal) {\n      if (!stateGoal.isValid()) {\n        stop()\n      } else if (stateGoal.hasChanged()) {\n        resetPath('goal_moved', false)\n      }\n    }\n\n    if (astarContext && astartTimedout) {\n      const results = astarContext.compute()\n      results.path = postProcessPath(results.path)\n      pathFromPlayer(results.path)\n      bot.emit('path_update', results)\n      path = results.path\n      astartTimedout = results.status === 'partial'\n    }\n\n    if (bot.pathfinder.LOSWhenPlacingBlocks && returningPos) {\n      if (!moveToBlock(returningPos)) return\n      returningPos = null\n    }\n\n    if (path.length === 0) {\n      lastNodeTime = performance.now()\n      if (stateGoal && stateMovements) {\n        if (stateGoal.isEnd(bot.entity.position.floored())) {\n          if (!dynamicGoal) {\n            bot.emit('goal_reached', stateGoal)\n            stateGoal = null\n            fullStop()\n          }\n        } else if (!pathUpdated) {\n          const results = bot.pathfinder.getPathTo(stateMovements, stateGoal)\n          bot.emit('path_update', results)\n          path = results.path\n          astartTimedout = results.status === 'partial'\n          pathUpdated = true\n        }\n      }\n    }\n\n    if (path.length === 0) {\n      return\n    }\n\n    let nextPoint = path[0]\n    const p = bot.entity.position\n\n    // Handle digging\n    if (digging || nextPoint.toBreak.length > 0) {\n      if (!digging && bot.entity.onGround) {\n        digging = true\n        const b = nextPoint.toBreak.shift()\n        const block = bot.blockAt(new Vec3(b.x, b.y, b.z), false)\n        const tool = bot.pathfinder.bestHarvestTool(block)\n        fullStop()\n\n        const digBlock = () => {\n          bot.dig(block, true)\n            .catch(_ignoreError => {\n              resetPath('dig_error')\n            })\n            .then(function () {\n              lastNodeTime = performance.now()\n              digging = false\n            })\n        }\n\n        if (!tool) {\n          digBlock()\n        } else {\n          bot.equip(tool, 'hand')\n            .catch(_ignoreError => {})\n            .then(() => digBlock())\n        }\n      }\n      return\n    }\n    // Handle block placement\n    // TODO: sneak when placing or make sure the block is not interactive\n    if (placing || nextPoint.toPlace.length > 0) {\n      if (!placing) {\n        placing = true\n        placingBlock = nextPoint.toPlace.shift()\n        fullStop()\n      }\n\n      // Open gates or doors\n      if (placingBlock?.useOne) {\n        if (!lockUseBlock.tryAcquire()) return\n        bot.activateBlock(bot.blockAt(new Vec3(placingBlock.x, placingBlock.y, placingBlock.z))).then(() => {\n          lockUseBlock.release()\n          placingBlock = nextPoint.toPlace.shift()\n        }, err => {\n          console.error(err)\n          lockUseBlock.release()\n        })\n        return\n      }\n      const block = stateMovements.getScaffoldingItem()\n      if (!block) {\n        resetPath('no_scaffolding_blocks')\n        return\n      }\n      if (bot.pathfinder.LOSWhenPlacingBlocks && placingBlock.y === bot.entity.position.floored().y - 1 && placingBlock.dy === 0) {\n        if (!moveToEdge(new Vec3(placingBlock.x, placingBlock.y, placingBlock.z), new Vec3(placingBlock.dx, 0, placingBlock.dz))) return\n      }\n      let canPlace = true\n      if (placingBlock.jump) {\n        bot.setControlState('jump', true)\n        canPlace = placingBlock.y + 1 < bot.entity.position.y\n      }\n      if (canPlace) {\n        if (!lockEquipItem.tryAcquire()) return\n        bot.equip(block, 'hand')\n          .then(function () {\n            lockEquipItem.release()\n            const refBlock = bot.blockAt(new Vec3(placingBlock.x, placingBlock.y, placingBlock.z), false)\n            if (!lockPlaceBlock.tryAcquire()) return\n            if (interactableBlocks.includes(refBlock.name)) {\n              bot.setControlState('sneak', true)\n            }\n            bot.placeBlock(refBlock, new Vec3(placingBlock.dx, placingBlock.dy, placingBlock.dz))\n              .then(function () {\n                // Dont release Sneak if the block placement was not successful\n                bot.setControlState('sneak', false)\n                if (bot.pathfinder.LOSWhenPlacingBlocks && placingBlock.returnPos) returningPos = placingBlock.returnPos.clone()\n              })\n              .catch(_ignoreError => {\n                resetPath('place_error')\n              })\n              .then(() => {\n                lockPlaceBlock.release()\n                placing = false\n                lastNodeTime = performance.now()\n              })\n          })\n          .catch(_ignoreError => {})\n      }\n      return\n    }\n\n    let dx = nextPoint.x - p.x\n    const dy = nextPoint.y - p.y\n    let dz = nextPoint.z - p.z\n    if (Math.abs(dx) <= 0.35 && Math.abs(dz) <= 0.35 && Math.abs(dy) < 1) {\n      // arrived at next point\n      lastNodeTime = performance.now()\n      if (stopPathing) {\n        stop()\n        return\n      }\n      path.shift()\n      if (path.length === 0) { // done\n        // If the block the bot is standing on is not a full block only checking for the floored position can fail as\n        // the distance to the goal can get greater then 0 when the vector is floored.\n        if (!dynamicGoal && stateGoal && (stateGoal.isEnd(p.floored()) || stateGoal.isEnd(p.floored().offset(0, 1, 0)))) {\n          bot.emit('goal_reached', stateGoal)\n          stateGoal = null\n        }\n        fullStop()\n        return\n      }\n      // not done yet\n      nextPoint = path[0]\n      if (nextPoint.toBreak.length > 0 || nextPoint.toPlace.length > 0) {\n        fullStop()\n        return\n      }\n      dx = nextPoint.x - p.x\n      dz = nextPoint.z - p.z\n    }\n\n    bot.look(Math.atan2(-dx, -dz), 0)\n    bot.setControlState('forward', true)\n    bot.setControlState('jump', false)\n\n    if (bot.entity.isInWater) {\n      bot.setControlState('jump', true)\n      bot.setControlState('sprint', false)\n    } else if (stateMovements.allowSprinting && physics.canStraightLine(path, true)) {\n      bot.setControlState('jump', false)\n      bot.setControlState('sprint', true)\n    } else if (stateMovements.allowSprinting && physics.canSprintJump(path)) {\n      bot.setControlState('jump', true)\n      bot.setControlState('sprint', true)\n    } else if (physics.canStraightLine(path)) {\n      bot.setControlState('jump', false)\n      bot.setControlState('sprint', false)\n    } else if (physics.canWalkJump(path)) {\n      bot.setControlState('jump', true)\n      bot.setControlState('sprint', false)\n    } else {\n      bot.setControlState('forward', false)\n      bot.setControlState('sprint', false)\n    }\n\n    // check for futility\n    if (performance.now() - lastNodeTime > 3500) {\n      // should never take this long to go to the next node\n      resetPath('stuck')\n    }\n  }\n}\n\nmodule.exports = {\n  pathfinder: inject,\n  Movements: require('./lib/movements'),\n  goals: require('./lib/goals')\n}\n"
  },
  {
    "path": "lib/astar.js",
    "content": "const { performance } = require('perf_hooks')\n\nconst Heap = require('./heap.js')\n\nclass PathNode {\n  constructor () {\n    this.data = null\n    this.g = 0\n    this.h = 0\n    this.f = 0\n    this.parent = null\n  }\n\n  set (data, g, h, parent = null) {\n    this.data = data\n    this.g = g\n    this.h = h\n    this.f = g + h\n    this.parent = parent\n    return this\n  }\n}\n\nfunction reconstructPath (node) {\n  const path = []\n  while (node.parent) {\n    path.push(node.data)\n    node = node.parent\n  }\n  return path.reverse()\n}\n\nclass AStar {\n  constructor (start, movements, goal, timeout, tickTimeout = 40, searchRadius = -1) {\n    this.startTime = performance.now()\n\n    this.movements = movements\n    this.goal = goal\n    this.timeout = timeout\n    this.tickTimeout = tickTimeout\n\n    this.closedDataSet = new Set()\n    this.openHeap = new Heap()\n    this.openDataMap = new Map()\n\n    const startNode = new PathNode().set(start, 0, goal.heuristic(start))\n    this.openHeap.push(startNode)\n    this.openDataMap.set(startNode.data.hash, startNode)\n    this.bestNode = startNode\n\n    this.maxCost = searchRadius < 0 ? -1 : startNode.h + searchRadius\n    this.visitedChunks = new Set()\n  }\n\n  makeResult (status, node) {\n    return {\n      status,\n      cost: node.g,\n      time: performance.now() - this.startTime,\n      visitedNodes: this.closedDataSet.size,\n      generatedNodes: this.closedDataSet.size + this.openHeap.size(),\n      path: reconstructPath(node),\n      context: this\n    }\n  }\n\n  compute () {\n    const computeStartTime = performance.now()\n    while (!this.openHeap.isEmpty()) {\n      if (performance.now() - computeStartTime > this.tickTimeout) { // compute time per tick\n        return this.makeResult('partial', this.bestNode)\n      }\n      if (performance.now() - this.startTime > this.timeout) { // total compute time\n        return this.makeResult('timeout', this.bestNode)\n      }\n      const node = this.openHeap.pop()\n      if (this.goal.isEnd(node.data)) {\n        return this.makeResult('success', node)\n      }\n      // not done yet\n      this.openDataMap.delete(node.data.hash)\n      this.closedDataSet.add(node.data.hash)\n      this.visitedChunks.add(`${node.data.x >> 4},${node.data.z >> 4}`)\n\n      const neighbors = this.movements.getNeighbors(node.data)\n      for (const neighborData of neighbors) {\n        if (this.closedDataSet.has(neighborData.hash)) {\n          continue // skip closed neighbors\n        }\n        const gFromThisNode = node.g + neighborData.cost\n        let neighborNode = this.openDataMap.get(neighborData.hash)\n        let update = false\n\n        const heuristic = this.goal.heuristic(neighborData)\n        if (this.maxCost > 0 && gFromThisNode + heuristic > this.maxCost) continue\n\n        if (neighborNode === undefined) {\n          // add neighbor to the open set\n          neighborNode = new PathNode()\n          // properties will be set later\n          this.openDataMap.set(neighborData.hash, neighborNode)\n        } else {\n          if (neighborNode.g < gFromThisNode) {\n            // skip this one because another route is faster\n            continue\n          }\n          update = true\n        }\n        // found a new or better route.\n        // update this neighbor with this node as its new parent\n        neighborNode.set(neighborData, gFromThisNode, heuristic, node)\n        if (neighborNode.h < this.bestNode.h) this.bestNode = neighborNode\n        if (update) {\n          this.openHeap.update(neighborNode)\n        } else {\n          this.openHeap.push(neighborNode)\n        }\n      }\n    }\n    // all the neighbors of every accessible node have been exhausted\n    return this.makeResult('noPath', this.bestNode)\n  }\n}\n\nmodule.exports = AStar\n"
  },
  {
    "path": "lib/goals.js",
    "content": "const { Vec3 } = require('vec3')\nconst { getShapeFaceCenters } = require('./shapes')\n\n// Goal base class\nclass Goal {\n  // Return the distance between node and the goal\n  heuristic (node) {\n    return 0\n  }\n\n  // Return true if the node has reach the goal\n  isEnd (node) {\n    return true\n  }\n\n  // Return true if the goal has changed and the current path\n  // should be invalidated and computed again\n  hasChanged () {\n    return false\n  }\n\n  // Returns true if the goal is still valid for the goal,\n  // for the GoalFollow this would be true if the entity is not null\n  isValid () {\n    return true\n  }\n}\n\n// One specific block that the player should stand inside at foot level\nclass GoalBlock extends Goal {\n  constructor (x, y, z) {\n    super()\n    this.x = Math.floor(x)\n    this.y = Math.floor(y)\n    this.z = Math.floor(z)\n  }\n\n  heuristic (node) {\n    const dx = this.x - node.x\n    const dy = this.y - node.y\n    const dz = this.z - node.z\n    return distanceXZ(dx, dz) + Math.abs(dy)\n  }\n\n  isEnd (node) {\n    return node.x === this.x && node.y === this.y && node.z === this.z\n  }\n}\n\n// A block position that the player should get within a certain radius of, used for following entities\nclass GoalNear extends Goal {\n  constructor (x, y, z, range) {\n    super()\n    this.x = Math.floor(x)\n    this.y = Math.floor(y)\n    this.z = Math.floor(z)\n    this.rangeSq = range * range\n  }\n\n  heuristic (node) {\n    const dx = this.x - node.x\n    const dy = this.y - node.y\n    const dz = this.z - node.z\n    return distanceXZ(dx, dz) + Math.abs(dy)\n  }\n\n  isEnd (node) {\n    const dx = this.x - node.x\n    const dy = this.y - node.y\n    const dz = this.z - node.z\n    return (dx * dx + dy * dy + dz * dz) <= this.rangeSq\n  }\n}\n\n// Useful for long-range goals that don't have a specific Y level\nclass GoalXZ extends Goal {\n  constructor (x, z) {\n    super()\n    this.x = Math.floor(x)\n    this.z = Math.floor(z)\n  }\n\n  heuristic (node) {\n    const dx = this.x - node.x\n    const dz = this.z - node.z\n    return distanceXZ(dx, dz)\n  }\n\n  isEnd (node) {\n    return node.x === this.x && node.z === this.z\n  }\n}\n\n// Useful for finding builds that you don't have an exact Y level for, just an approximate X and Z level\nclass GoalNearXZ extends Goal {\n  constructor (x, z, range) {\n    super()\n    this.x = Math.floor(x)\n    this.z = Math.floor(z)\n    this.rangeSq = range * range\n  }\n\n  heuristic (node) {\n    const dx = this.x - node.x\n    const dz = this.z - node.z\n    return distanceXZ(dx, dz)\n  }\n\n  isEnd (node) {\n    const dx = this.x - node.x\n    const dz = this.z - node.z\n    return (dx * dx + dz * dz) <= this.rangeSq\n  }\n}\n\n// Goal is a Y coordinate\nclass GoalY extends Goal {\n  constructor (y) {\n    super()\n    this.y = Math.floor(y)\n  }\n\n  heuristic (node) {\n    const dy = this.y - node.y\n    return Math.abs(dy)\n  }\n\n  isEnd (node) {\n    return node.y === this.y\n  }\n}\n\n// Don't get into the block, but get directly adjacent to it. Useful for chests.\nclass GoalGetToBlock extends Goal {\n  constructor (x, y, z) {\n    super()\n    this.x = Math.floor(x)\n    this.y = Math.floor(y)\n    this.z = Math.floor(z)\n  }\n\n  heuristic (node) {\n    const dx = node.x - this.x\n    const dy = node.y - this.y\n    const dz = node.z - this.z\n    return distanceXZ(dx, dz) + Math.abs(dy < 0 ? dy + 1 : dy)\n  }\n\n  isEnd (node) {\n    const dx = node.x - this.x\n    const dy = node.y - this.y\n    const dz = node.z - this.z\n    return Math.abs(dx) + Math.abs(dy < 0 ? dy + 1 : dy) + Math.abs(dz) === 1\n  }\n}\n\n// Path into a position were a blockface of block at x y z is visible.\nclass GoalLookAtBlock extends Goal {\n  constructor (pos, world, options = {}) {\n    super()\n    this.pos = pos\n    this.world = world\n    this.reach = options.reach || 4.5 // default survival: 4.5 creative: 5\n    this.entityHeight = options.entityHeight || 1.6\n  }\n\n  heuristic (node) {\n    const dx = node.x - this.pos.x\n    const dy = node.y - this.pos.y\n    const dz = node.z - this.pos.z\n    return distanceXZ(dx, dz) + Math.abs(dy < 0 ? dy + 1 : dy)\n  }\n\n  isEnd (node) {\n    if (node.distanceTo(this.pos.offset(0, this.entityHeight, 0)) > this.reach) return false\n    // 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\n    // this could be false for blocks that have a smaller bounding box then 1x1x1\n    const dx = node.x - (this.pos.x + 0.5)\n    const dy = node.y + this.entityHeight - (this.pos.y + 0.5) // -0.5 because the bot position is calculated from the block position that is inside its feet so 0.5 - 1 = -0.5\n    const dz = node.z - (this.pos.z + 0.5)\n    // Check y first then x and z\n    const visibleFaces = {\n      y: Math.sign(Math.abs(dy) > 0.5 ? dy : 0),\n      x: Math.sign(Math.abs(dx) > 0.5 ? dx : 0),\n      z: Math.sign(Math.abs(dz) > 0.5 ? dz : 0)\n    }\n    const validFaces = []\n    for (const i in visibleFaces) {\n      if (!visibleFaces[i]) {\n        // skip as this face is not visible\n        continue\n      }\n      const targetPos = new Vec3(this.pos.x, this.pos.y, this.pos.z).offset(0.5 + (i === 'x' ? visibleFaces[i] * 0.5 : 0), 0.5 + (i === 'y' ? visibleFaces[i] * 0.5 : 0), 0.5 + (i === 'z' ? visibleFaces[i] * 0.5 : 0))\n      const startPos = new Vec3(node.x + 0.5, node.y + this.entityHeight, node.z + 0.5)\n      const rayPos = this.world.raycast(startPos, targetPos.clone().subtract(startPos).normalize(), this.reach)?.position\n      if (rayPos && rayPos.x === this.pos.x && rayPos.y === this.pos.y && rayPos.z === this.pos.z) {\n        validFaces.push({\n          face: rayPos.face,\n          targetPos\n        })\n      }\n    }\n    return validFaces.length !== 0\n  }\n}\n\n// Path into a position were a blockface of block at x y z is visible.\n// You'll manually need to break the block. THIS WONT BREAK IT\nclass GoalBreakBlock extends Goal {\n  constructor (x, y, z, bot, options = {}) {\n    super()\n    this.goal = new GoalLookAtBlock(new Vec3(x, y, z), bot, options)\n  }\n\n  isEnd (node) {\n    return this.goal.isEnd(node)\n  }\n\n  heuristic (node) {\n    return this.goal.heuristic(node)\n  }\n}\n\n// A composite of many goals, any one of which satisfies the composite.\n// For example, a GoalCompositeAny of block goals for every oak log in loaded\n// chunks would result in it pathing to the easiest oak log to get to\nclass GoalCompositeAny extends Goal {\n  constructor (goals = []) {\n    super()\n    this.goals = goals\n  }\n\n  push (goal) {\n    this.goals.push(goal)\n  }\n\n  heuristic (node) {\n    let min = Number.MAX_VALUE\n    for (const i in this.goals) {\n      min = Math.min(min, this.goals[i].heuristic(node))\n    }\n    return min\n  }\n\n  isEnd (node) {\n    for (const i in this.goals) {\n      if (this.goals[i].isEnd(node)) return true\n    }\n    return false\n  }\n\n  hasChanged () {\n    for (const i in this.goals) {\n      if (this.goals[i].hasChanged()) return true\n    }\n    return false\n  }\n\n  isValid () {\n    return this.goals.reduce((pre, curr) => pre && curr.isValid(), true)\n  }\n}\n\n// A composite of many goals, all of them needs to be satisfied.\nclass GoalCompositeAll extends Goal {\n  constructor (goals = []) {\n    super()\n    this.goals = goals\n  }\n\n  push (goal) {\n    this.goals.push(goal)\n  }\n\n  heuristic (node) {\n    let max = Number.MIN_VALUE\n    for (const i in this.goals) {\n      max = Math.max(max, this.goals[i].heuristic(node))\n    }\n    return max\n  }\n\n  isEnd (node) {\n    for (const i in this.goals) {\n      if (!this.goals[i].isEnd(node)) return false\n    }\n    return true\n  }\n\n  hasChanged () {\n    for (const i in this.goals) {\n      if (this.goals[i].hasChanged()) return true\n    }\n    return false\n  }\n\n  isValid () {\n    return this.goals.reduce((pre, curr) => pre && curr.isValid(), true)\n  }\n}\n\nclass GoalInvert extends Goal {\n  constructor (goal) {\n    super()\n    this.goal = goal\n  }\n\n  heuristic (node) {\n    return -this.goal.heuristic(node)\n  }\n\n  isEnd (node) {\n    return !this.goal.isEnd(node)\n  }\n\n  hasChanged () {\n    return this.goal.hasChanged()\n  }\n\n  isValid () {\n    return this.goal.isValid()\n  }\n}\n\nclass GoalFollow extends Goal {\n  constructor (entity, range) {\n    super()\n    this.entity = entity\n    this.x = Math.floor(entity.position.x)\n    this.y = Math.floor(entity.position.y)\n    this.z = Math.floor(entity.position.z)\n    this.rangeSq = range * range\n  }\n\n  heuristic (node) {\n    const dx = this.x - node.x\n    const dy = this.y - node.y\n    const dz = this.z - node.z\n    return distanceXZ(dx, dz) + Math.abs(dy)\n  }\n\n  isEnd (node) {\n    const dx = this.x - node.x\n    const dy = this.y - node.y\n    const dz = this.z - node.z\n    return (dx * dx + dy * dy + dz * dz) <= this.rangeSq\n  }\n\n  hasChanged () {\n    const p = this.entity.position.floored()\n    const dx = this.x - p.x\n    const dy = this.y - p.y\n    const dz = this.z - p.z\n    if ((dx * dx + dy * dy + dz * dz) > this.rangeSq) {\n      this.x = p.x\n      this.y = p.y\n      this.z = p.z\n      return true\n    }\n    return false\n  }\n\n  isValid () {\n    return this.entity != null\n  }\n}\n\nfunction distanceXZ (dx, dz) {\n  dx = Math.abs(dx)\n  dz = Math.abs(dz)\n  return Math.abs(dx - dz) + Math.min(dx, dz) * Math.SQRT2\n}\n\n/**\n * Options:\n * - range - maximum distance from the clicked face\n * - faces - the directions of the faces the player can click\n * - facing - the direction the player must be facing\n * - facing3D - boolean, facing is 3D (true) or 2D (false)\n * - half - 'top' or 'bottom', the half that must be clicked\n * - LOS - true or false, should the bot have line of sight off the placement face. Default true.\n */\nclass GoalPlaceBlock extends Goal {\n  constructor (pos, world, options) {\n    super()\n    this.pos = pos.floored()\n    this.world = world\n    this.options = options\n    if (!this.options.range) this.options.range = 5\n    if (!('LOS' in this.options)) this.options.LOS = true\n    if (!this.options.faces) {\n      this.options.faces = [new Vec3(0, -1, 0), new Vec3(0, 1, 0), new Vec3(0, 0, -1), new Vec3(0, 0, 1), new Vec3(-1, 0, 0), new Vec3(1, 0, 0)]\n    }\n    this.options.facing = ['north', 'east', 'south', 'west', 'up', 'down'].indexOf(this.options.facing)\n    this.facesPos = []\n    for (const dir of this.options.faces) {\n      const ref = this.pos.plus(dir)\n      const refBlock = this.world.getBlock(ref)\n      if (!refBlock) continue\n      for (const center of getShapeFaceCenters(refBlock.shapes, dir.scaled(-1), this.options.half)) {\n        this.facesPos.push([dir, center.add(ref), ref])\n      }\n    }\n  }\n\n  heuristic (node) {\n    const dx = node.x - this.pos.x\n    const dy = node.y - this.pos.y\n    const dz = node.z - this.pos.z\n    return distanceXZ(dx, dz) + Math.abs(dy < 0 ? dy + 1 : dy)\n  }\n\n  isEnd (node) {\n    if (this.isStandingIn(node)) return false\n    const headPos = node.offset(0.5, 1.6, 0.5)\n    return this.getFaceAndRef(headPos) !== null\n  }\n\n  getFaceAndRef (headPos) {\n    for (const [face, to, ref] of this.facesPos) {\n      const dir = to.minus(headPos)\n      if (dir.norm() > this.options.range) continue\n      if (!this.checkFacing(dir)) continue\n\n      if (!this.options.LOS) {\n        return { face, to, ref }\n      }\n\n      const block = this.world.raycast(headPos, dir.normalize(), this.options.range)\n      if (block && block.position.equals(ref) && block.face === vectorToDirection(face.scaled(-1))) {\n        return { face, to, ref }\n      }\n    }\n    return null\n  }\n\n  checkFacing (dir) {\n    if (this.options.facing < 0) return true\n\n    if (this.options.facing3D) {\n      const dH = Math.sqrt(dir.x * dir.x + dir.z * dir.z)\n      const vAngle = Math.atan2(dir.y, dH) * 180 / Math.PI\n      if (vAngle > 45) return this.options.facing === 4\n      if (vAngle < -45) return this.options.facing === 5\n    }\n    const angle = Math.atan2(dir.x, -dir.z) * 180 / Math.PI + 180 // Convert to [0,360[\n    const facing = Math.floor(angle / 90 + 0.5) & 0x3\n\n    if (this.options.facing === facing) return true\n    return false\n  }\n\n  isStandingIn (node) {\n    const dx = node.x - this.pos.x\n    const dy = node.y - this.pos.y\n    const dz = node.z - this.pos.z\n    return (Math.abs(dx) + Math.abs(dy < 0 ? dy + 1 : dy) + Math.abs(dz)) < 1\n  }\n}\n\nfunction vectorToDirection (v) {\n  if (v.y < 0) {\n    return 0\n  } else if (v.y > 0) {\n    return 1\n  } else if (v.z < 0) {\n    return 2\n  } else if (v.z > 0) {\n    return 3\n  } else if (v.x < 0) {\n    return 4\n  } else if (v.x > 0) {\n    return 5\n  }\n}\n\nmodule.exports = {\n  Goal,\n  GoalBlock,\n  GoalNear,\n  GoalXZ,\n  GoalNearXZ,\n  GoalY,\n  GoalGetToBlock,\n  GoalCompositeAny,\n  GoalCompositeAll,\n  GoalInvert,\n  GoalFollow,\n  GoalPlaceBlock,\n  GoalBreakBlock,\n  GoalLookAtBlock\n}\n"
  },
  {
    "path": "lib/goto.js",
    "content": "function error (name, message) {\n  const err = new Error(message)\n  err.name = name\n  return err\n}\n\n/**\n   * Adds a easy-to-use API wrapper for quickly executing a goal and running\n   * a callback when that goal is reached. This function serves to remove a\n   * lot of boilerplate code for quickly executing a goal.\n   *\n   * @param {Bot} bot - The bot.\n   * @param {Goal} goal - The goal to execute.\n   * @returns {Promise} - resolves on success, rejects on error\n   */\nfunction goto (bot, goal) {\n  return new Promise((resolve, reject) => {\n    function goalReached () {\n      cleanup()\n    }\n\n    function noPathListener (results) {\n      if (results.path.length === 0) {\n        cleanup()\n      } else if (results.status === 'noPath') {\n        cleanup(error('NoPath', 'No path to the goal!'))\n      } else if (results.status === 'timeout') {\n        cleanup(error('Timeout', 'Took to long to decide path to goal!'))\n      }\n    }\n\n    function goalChangedListener (newGoal) {\n      if (newGoal !== goal) {\n        cleanup(error('GoalChanged', 'The goal was changed before it could be completed!'))\n      }\n    }\n\n    function pathStopped () {\n      cleanup(error('PathStopped', 'Path was stopped before it could be completed! Thus, the desired goal was not reached.'))\n    }\n\n    function cleanup (err) {\n      bot.removeListener('goal_reached', goalReached)\n      bot.removeListener('path_update', noPathListener)\n      bot.removeListener('goal_updated', goalChangedListener)\n      bot.removeListener('path_stop', pathStopped)\n\n      // Run callback on next event stack to let pathfinder properly cleanup,\n      // otherwise chaining waypoints does not work properly.\n      setTimeout(() => {\n        if (err) {\n          reject(err)\n        } else {\n          resolve()\n        }\n      }, 0)\n    }\n\n    bot.on('path_stop', pathStopped)\n    bot.on('goal_reached', goalReached)\n    bot.on('path_update', noPathListener)\n    bot.on('goal_updated', goalChangedListener)\n    bot.pathfinder.setGoal(goal)\n  })\n}\n\nmodule.exports = goto\n"
  },
  {
    "path": "lib/heap.js",
    "content": "class BinaryHeapOpenSet {\n  constructor () {\n    // Initialing the array heap and adding a dummy element at index 0\n    this.heap = [null]\n  }\n\n  size () {\n    return this.heap.length - 1\n  }\n\n  isEmpty () {\n    return this.heap.length === 1\n  }\n\n  push (val) {\n    // Inserting the new node at the end of the heap array\n    this.heap.push(val)\n\n    // Finding the correct position for the new node\n    let current = this.heap.length - 1\n    let parent = current >>> 1\n\n    // Traversing up the parent node until the current node is greater than the parent\n    while (current > 1 && this.heap[parent].f > this.heap[current].f) {\n      [this.heap[parent], this.heap[current]] = [this.heap[current], this.heap[parent]]\n      current = parent\n      parent = current >>> 1\n    }\n  }\n\n  update (val) {\n    let current = this.heap.indexOf(val)\n    let parent = current >>> 1\n\n    // Traversing up the parent node until the current node is greater than the parent\n    while (current > 1 && this.heap[parent].f > this.heap[current].f) {\n      [this.heap[parent], this.heap[current]] = [this.heap[current], this.heap[parent]]\n      current = parent\n      parent = current >>> 1\n    }\n  }\n\n  pop () {\n    // Smallest element is at the index 1 in the heap array\n    const smallest = this.heap[1]\n\n    this.heap[1] = this.heap[this.heap.length - 1]\n    this.heap.splice(this.heap.length - 1)\n\n    const size = this.heap.length - 1\n\n    if (size < 2) return smallest\n\n    const val = this.heap[1]\n    let index = 1\n    let smallerChild = 2\n    const cost = val.f\n    do {\n      let smallerChildNode = this.heap[smallerChild]\n      if (smallerChild < size - 1) {\n        const rightChildNode = this.heap[smallerChild + 1]\n        if (smallerChildNode.f > rightChildNode.f) {\n          smallerChild++\n          smallerChildNode = rightChildNode\n        }\n      }\n      if (cost <= smallerChildNode.f) {\n        break\n      }\n      this.heap[index] = smallerChildNode\n      this.heap[smallerChild] = val\n      index = smallerChild\n\n      smallerChild *= 2\n    } while (smallerChild <= size)\n\n    return smallest\n  }\n}\n\nmodule.exports = BinaryHeapOpenSet\n"
  },
  {
    "path": "lib/interactable.json",
    "content": "[\n  \"acacia_door\",\n  \"acacia_fence_gate\",\n  \"acacia_button\",\n  \"acacia_trapdoor\",\n  \"anvil\",\n  \"armor_stand\",\n  \"barrel\",\n  \"beacon\",\n  \"bed_block\",\n  \"bell\",\n  \"birch_boat\",\n  \"birch_button\",\n  \"birch_door\",\n  \"birch_fence_gate\",\n  \"birch_trapdoor\",\n  \"black_bed\",\n  \"black_shulker_box\",\n  \"blast_furnace\",\n  \"blue_bed\",\n  \"blue_shulker_box\",\n  \"brewing_stand\",\n  \"brown_bed\",\n  \"brown_shulker_box\",\n  \"campfire\",\n  \"cauldron\",\n  \"chest\",\n  \"chest_minecart\",\n  \"chipped_anvil\",\n  \"command\",\n  \"command_block\",\n  \"command_block_minecart\",\n  \"comparator\",\n  \"composter\",\n  \"crafting_table\",\n  \"cyan_bed\",\n  \"cyan_shulker_box\",\n  \"damaged_anvil\",\n  \"dark_oak_boat\",\n  \"dark_oak_button\",\n  \"dark_oak_fence_gate\",\n  \"dark_oak_trapdoor\",\n  \"dark_oak_door\",\n  \"daylight_detector\",\n  \"daylight_detector_inverted\",\n  \"diode\",\n  \"diode_block_off\",\n  \"diode_block_on\",\n  \"dispenser\",\n  \"door\",\n  \"dragon_egg\",\n  \"dropper\",\n  \"enchanting_table\",\n  \"enchantment_table\",\n  \"end_crystal\",\n  \"end_portal_frame\",\n  \"ender_portal_frame\",\n  \"ender_chest\",\n  \"explosive_minecart\",\n  \"farmland\",\n  \"fence_gate\",\n  \"fletching_table\",\n  \"flower_pot\",\n  \"furnace\",\n  \"furnace_minecart\",\n  \"gray_bed\",\n  \"gray_shulker_box\",\n  \"green_bed\",\n  \"green_shulker_box\",\n  \"hopper\",\n  \"hopper_minecart\",\n  \"iron_door\",\n  \"iron_trapdoor\",\n  \"item_frame\",\n  \"jukebox\",\n  \"jungle_button\",\n  \"jungle_boat\",\n  \"jungle_door\",\n  \"jungle_fence_gate\",\n  \"jungle_trapdoor\",\n  \"lever\",\n  \"light_blue_bed\",\n  \"light_blue_shulker_box\",\n  \"light_gray_bed\",\n  \"light_gray_shulker_box\",\n  \"lime_bed\",\n  \"lime_shulker_box\",\n  \"magenta_bed\",\n  \"magenta_shulker_box\",\n  \"minecart\",\n  \"note_block\",\n  \"oak_boat\",\n  \"oak_button\",\n  \"oak_door\",\n  \"oak_fence_gate\",\n  \"oak_trapdoor\",\n  \"orange_bed\",\n  \"orange_shulker_box\",\n  \"pink_bed\",\n  \"pink_shulker_box\",\n  \"powered_minecart\",\n  \"purple_bed\",\n  \"purple_shulker_box\",\n  \"red_bed\",\n  \"red_shulker_box\",\n  \"redstone_ore\",\n  \"redstone_comparator_off\",\n  \"redstone_comparator_on\",\n  \"repeating_command_block\",\n  \"repeater\",\n  \"powered_repeater\",\n  \"unpowered_repeater\",\n  \"redstone_torch\",\n  \"saddle\",\n  \"shulker_box\",\n  \"sign\",\n  \"sign_post\",\n  \"smithing_table\",\n  \"smoker\",\n  \"spruce_boat\",\n  \"spruce_button\",\n  \"spruce_door\",\n  \"spruce_fence_gate\",\n  \"stonecutter\",\n  \"stone_button\",\n  \"storage_minecart\",\n  \"tnt_minecart\",\n  \"tnt\",\n  \"trap_door\",\n  \"trapped_chest\",\n  \"white_bed\",\n  \"white_shulker_box\",\n  \"wood_button\",\n  \"yellow_bed\",\n  \"yelow_shulker_box\"\n]"
  },
  {
    "path": "lib/lock.js",
    "content": "const { EventEmitter, on } = require('events')\n\nclass Lock {\n  constructor () {\n    this._locked = false\n    this._emitter = new EventEmitter()\n  }\n\n  /**\n   * Synchronous. Returns true if the lock was acquired. Return false if the lock is already held by something else.\n   * @returns {boolean}\n   */\n  tryAcquire () {\n    if (!this._locked) {\n      this._locked = true\n      return true\n    }\n    return false\n  }\n\n  /**\n   * Asynchronous. Resolves when the lock was acquired.\n   * @returns {Promise<void>}\n   */\n  async acquire () {\n    if (!this._locked) {\n      this._locked = true\n      return\n    }\n\n    // Cannot use for await without a variable. But the variable is never used. So eslint complains ¯\\_(ツ)_/¯\n    for await (const _ of on(this._emitter, 'release')) { // eslint-disable-line\n      if (!this._locked) {\n        this._locked = true\n        return\n      }\n    }\n  }\n\n  /**\n   * Releases the lock.\n   */\n  release () {\n    this._locked = false\n    setImmediate(() => this._emitter.emit('release'))\n  }\n}\n\nmodule.exports = Lock\n"
  },
  {
    "path": "lib/move.js",
    "content": "const { Vec3 } = require('vec3')\n\nclass Move extends Vec3 {\n  constructor (x, y, z, remainingBlocks, cost, toBreak = [], toPlace = [], parkour = false) {\n    super(Math.floor(x), Math.floor(y), Math.floor(z))\n    this.remainingBlocks = remainingBlocks\n    this.cost = cost\n    this.toBreak = toBreak\n    this.toPlace = toPlace\n    this.parkour = parkour\n\n    this.hash = this.x + ',' + this.y + ',' + this.z\n  }\n}\n\nmodule.exports = Move\n"
  },
  {
    "path": "lib/movements.js",
    "content": "const { Vec3 } = require('vec3')\nconst nbt = require('prismarine-nbt')\nconst Move = require('./move')\n\nconst cardinalDirections = [\n  { x: -1, z: 0 }, // West\n  { x: 1, z: 0 }, // East\n  { x: 0, z: -1 }, // North\n  { x: 0, z: 1 } // South\n]\nconst diagonalDirections = [\n  { x: -1, z: -1 },\n  { x: -1, z: 1 },\n  { x: 1, z: -1 },\n  { x: 1, z: 1 }\n]\n\nclass Movements {\n  constructor (bot) {\n    const registry = bot.registry\n    this.bot = bot\n\n    this.canDig = true\n    this.digCost = 1\n    this.placeCost = 1\n    this.liquidCost = 1\n    this.entityCost = 1\n\n    this.dontCreateFlow = true\n    this.dontMineUnderFallingBlock = true\n    this.allow1by1towers = true\n    this.allowFreeMotion = false\n    this.allowParkour = true\n    this.allowSprinting = true\n    this.allowEntityDetection = true\n\n    this.entitiesToAvoid = new Set()\n    this.passableEntities = new Set(require('./passableEntities.json'))\n    this.interactableBlocks = new Set(require('./interactable.json'))\n\n    this.blocksCantBreak = new Set()\n    this.blocksCantBreak.add(registry.blocksByName.chest.id)\n\n    registry.blocksArray.forEach(block => {\n      if (block.diggable) return\n      this.blocksCantBreak.add(block.id)\n    })\n\n    this.blocksToAvoid = new Set()\n    this.blocksToAvoid.add(registry.blocksByName.fire.id)\n    if (registry.blocksByName.cobweb) this.blocksToAvoid.add(registry.blocksByName.cobweb.id)\n    if (registry.blocksByName.web) this.blocksToAvoid.add(registry.blocksByName.web.id)\n    this.blocksToAvoid.add(registry.blocksByName.lava.id)\n\n    this.liquids = new Set()\n    this.liquids.add(registry.blocksByName.water.id)\n    this.liquids.add(registry.blocksByName.lava.id)\n\n    this.gravityBlocks = new Set()\n    this.gravityBlocks.add(registry.blocksByName.sand.id)\n    this.gravityBlocks.add(registry.blocksByName.gravel.id)\n\n    this.climbables = new Set()\n    this.climbables.add(registry.blocksByName.ladder.id)\n    // this.climbables.add(registry.blocksByName.vine.id)\n    this.emptyBlocks = new Set()\n\n    this.replaceables = new Set()\n    this.replaceables.add(registry.blocksByName.air.id)\n    if (registry.blocksByName.cave_air) this.replaceables.add(registry.blocksByName.cave_air.id)\n    if (registry.blocksByName.void_air) this.replaceables.add(registry.blocksByName.void_air.id)\n    this.replaceables.add(registry.blocksByName.water.id)\n    this.replaceables.add(registry.blocksByName.lava.id)\n\n    this.scafoldingBlocks = []\n    this.scafoldingBlocks.push(registry.itemsByName.dirt.id)\n    this.scafoldingBlocks.push(registry.itemsByName.cobblestone.id)\n\n    const Block = require('prismarine-block')(bot.registry)\n    this.fences = new Set()\n    this.carpets = new Set()\n    this.openable = new Set()\n    registry.blocksArray.map(x => Block.fromStateId(x.minStateId, 0)).forEach(block => {\n      if (block.shapes.length > 0) {\n        // Fences or any block taller than 1, they will be considered as non-physical to avoid\n        // trying to walk on them\n        if (block.shapes[0][4] > 1) this.fences.add(block.type)\n        // Carpets or any blocks smaller than 0.1, they will be considered as safe to walk in\n        if (block.shapes[0][4] < 0.1) this.carpets.add(block.type)\n      } else if (block.shapes.length === 0) {\n        this.emptyBlocks.add(block.type)\n      }\n    })\n    registry.blocksArray.forEach(block => {\n      if (this.interactableBlocks.has(block.name) && block.name.toLowerCase().includes('gate') && !block.name.toLowerCase().includes('iron')) {\n        // console.info(block)\n        this.openable.add(block.id)\n      }\n    })\n\n    this.canOpenDoors = false // Causes issues. Probably due to none paper servers.\n\n    this.exclusionAreasStep = []\n    this.exclusionAreasBreak = []\n    this.exclusionAreasPlace = []\n\n    this.maxDropDown = 4\n    this.infiniteLiquidDropdownDistance = true\n\n    this.entityIntersections = {}\n  }\n\n  exclusionPlace (block) {\n    if (this.exclusionAreasPlace.length === 0) return 0\n    let weight = 0\n    for (const a of this.exclusionAreasPlace) {\n      weight += a(block)\n    }\n    return weight\n  }\n\n  exclusionStep (block) {\n    if (this.exclusionAreasStep.length === 0) return 0\n    let weight = 0\n    for (const a of this.exclusionAreasStep) {\n      weight += a(block)\n    }\n    return weight\n  }\n\n  exclusionBreak (block) {\n    if (this.exclusionAreasBreak.length === 0) return 0\n    let weight = 0\n    for (const a of this.exclusionAreasBreak) {\n      weight += a(block)\n    }\n    return weight\n  }\n\n  countScaffoldingItems () {\n    let count = 0\n    const items = this.bot.inventory.items()\n    for (const id of this.scafoldingBlocks) {\n      for (const j in items) {\n        const item = items[j]\n        if (item.type === id) count += item.count\n      }\n    }\n    return count\n  }\n\n  getScaffoldingItem () {\n    const items = this.bot.inventory.items()\n    for (const id of this.scafoldingBlocks) {\n      for (const j in items) {\n        const item = items[j]\n        if (item.type === id) return item\n      }\n    }\n    return null\n  }\n\n  clearCollisionIndex () {\n    this.entityIntersections = {}\n  }\n\n  /**\n   * Finds blocks intersected by entity bounding boxes\n   * and sets the number of ents intersecting in a dict.\n   * Ignores entities that do not affect block placement\n   */\n  updateCollisionIndex () {\n    for (const ent of Object.values(this.bot.entities)) {\n      if (ent === this.bot.entity) { continue }\n\n      const avoidedEnt = this.entitiesToAvoid.has(ent.name)\n      if (avoidedEnt || !this.passableEntities.has(ent.name)) {\n        const entSquareRadius = ent.width / 2.0\n        const minY = Math.floor(ent.position.y)\n        const maxY = Math.ceil(ent.position.y + ent.height)\n        const minX = Math.floor(ent.position.x - entSquareRadius)\n        const maxX = Math.ceil(ent.position.x + entSquareRadius)\n        const minZ = Math.floor(ent.position.z - entSquareRadius)\n        const maxZ = Math.ceil(ent.position.z + entSquareRadius)\n\n        const cost = avoidedEnt ? 100 : 1\n\n        for (let y = minY; y < maxY; y++) {\n          for (let x = minX; x < maxX; x++) {\n            for (let z = minZ; z < maxZ; z++) {\n              this.entityIntersections[`${x},${y},${z}`] = this.entityIntersections[`${x},${y},${z}`] ?? 0\n              this.entityIntersections[`${x},${y},${z}`] += cost // More ents = more weight\n            }\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Gets number of entities who's bounding box intersects the node + offset\n   * @param {import('vec3').Vec3} pos node position\n   * @param {number} dx X axis offset\n   * @param {number} dy Y axis offset\n   * @param {number} dz Z axis offset\n   * @returns {number} Number of entities intersecting block\n   */\n  getNumEntitiesAt (pos, dx, dy, dz) {\n    if (this.allowEntityDetection === false) return 0\n    if (!pos) return 0\n    const y = pos.y + dy\n    const x = pos.x + dx\n    const z = pos.z + dz\n\n    return this.entityIntersections[`${x},${y},${z}`] ?? 0\n  }\n\n  getBlock (pos, dx, dy, dz) {\n    const b = pos ? this.bot.blockAt(new Vec3(pos.x + dx, pos.y + dy, pos.z + dz), false) : null\n    if (!b) {\n      return {\n        replaceable: false,\n        canFall: false,\n        safe: false,\n        physical: false,\n        liquid: false,\n        climbable: false,\n        height: dy,\n        openable: false\n      }\n    }\n    b.climbable = this.climbables.has(b.type)\n    b.safe = (b.boundingBox === 'empty' || b.climbable || this.carpets.has(b.type)) && !this.blocksToAvoid.has(b.type)\n    b.physical = b.boundingBox === 'block' && !this.fences.has(b.type)\n    b.replaceable = this.replaceables.has(b.type) && !b.physical\n    b.liquid = this.liquids.has(b.type)\n    b.height = pos.y + dy\n    b.canFall = this.gravityBlocks.has(b.type)\n    b.openable = this.openable.has(b.type)\n\n    for (const shape of b.shapes) {\n      b.height = Math.max(b.height, pos.y + dy + shape[4])\n    }\n    return b\n  }\n\n  /**\n   * Takes into account if the block is within a break exclusion area.\n   * @param {import('prismarine-block').Block} block\n   * @returns\n   */\n  safeToBreak (block) {\n    if (!this.canDig) {\n      return false\n    }\n\n    if (this.dontCreateFlow) {\n      // false if next to liquid\n      if (this.getBlock(block.position, 0, 1, 0).liquid) return false\n      if (this.getBlock(block.position, -1, 0, 0).liquid) return false\n      if (this.getBlock(block.position, 1, 0, 0).liquid) return false\n      if (this.getBlock(block.position, 0, 0, -1).liquid) return false\n      if (this.getBlock(block.position, 0, 0, 1).liquid) return false\n    }\n\n    if (this.dontMineUnderFallingBlock) {\n      // TODO: Determine if there are other blocks holding the entity up\n      if (this.getBlock(block.position, 0, 1, 0).canFall || (this.getNumEntitiesAt(block.position, 0, 1, 0) > 0)) {\n        return false\n      }\n    }\n\n    return block.type && !this.blocksCantBreak.has(block.type) && this.exclusionBreak(block) < 100\n  }\n\n  /**\n   * Takes into account if the block is within the stepExclusionAreas. And returns 100 if a block to be broken is within break exclusion areas.\n   * @param {import('prismarine-block').Block} block block\n   * @param {[]} toBreak\n   * @returns {number}\n   */\n  safeOrBreak (block, toBreak) {\n    let cost = 0\n    cost += this.exclusionStep(block) // Is excluded so can't move or break\n    cost += this.getNumEntitiesAt(block.position, 0, 0, 0) * this.entityCost\n    if (block.safe) return cost\n    if (!this.safeToBreak(block)) return 100 // Can't break, so can't move\n    toBreak.push(block.position)\n\n    if (block.physical) cost += this.getNumEntitiesAt(block.position, 0, 1, 0) * this.entityCost // Add entity cost if there is an entity above (a breakable block) that will fall\n\n    const tool = this.bot.pathfinder.bestHarvestTool(block)\n    const enchants = (tool && tool.nbt) ? nbt.simplify(tool.nbt).Enchantments : []\n    const effects = this.bot.entity.effects\n    const digTime = block.digTime(tool ? tool.type : null, false, false, false, enchants, effects)\n    const laborCost = (1 + 3 * digTime / 1000) * this.digCost\n    cost += laborCost\n    return cost\n  }\n\n  getMoveJumpUp (node, dir, neighbors) {\n    const blockA = this.getBlock(node, 0, 2, 0)\n    const blockH = this.getBlock(node, dir.x, 2, dir.z)\n    const blockB = this.getBlock(node, dir.x, 1, dir.z)\n    const blockC = this.getBlock(node, dir.x, 0, dir.z)\n\n    let cost = 2 // move cost (move+jump)\n    const toBreak = []\n    const toPlace = []\n\n    if (blockA.physical && (this.getNumEntitiesAt(blockA.position, 0, 1, 0) > 0)) return // Blocks A, B and H are above C, D and the player's space, we need to make sure there are no entities that will fall down onto our building space if we break them\n    if (blockH.physical && (this.getNumEntitiesAt(blockH.position, 0, 1, 0) > 0)) return\n    if (blockB.physical && !blockH.physical && !blockC.physical && (this.getNumEntitiesAt(blockB.position, 0, 1, 0) > 0)) return // It is fine if an ent falls on B so long as we don't need to replace block C\n\n    if (!blockC.physical) {\n      if (node.remainingBlocks === 0) return // not enough blocks to place\n\n      if (this.getNumEntitiesAt(blockC.position, 0, 0, 0) > 0) return // Check for any entities in the way of a block placement\n\n      const blockD = this.getBlock(node, dir.x, -1, dir.z)\n      if (!blockD.physical) {\n        if (node.remainingBlocks === 1) return // not enough blocks to place\n\n        if (this.getNumEntitiesAt(blockD.position, 0, 0, 0) > 0) return // Check for any entities in the way of a block placement\n\n        if (!blockD.replaceable) {\n          if (!this.safeToBreak(blockD)) return\n          cost += this.exclusionBreak(blockD)\n          toBreak.push(blockD.position)\n        }\n        cost += this.exclusionPlace(blockD)\n        toPlace.push({ x: node.x, y: node.y - 1, z: node.z, dx: dir.x, dy: 0, dz: dir.z, returnPos: new Vec3(node.x, node.y, node.z) })\n        cost += this.placeCost // additional cost for placing a block\n      }\n\n      if (!blockC.replaceable) {\n        if (!this.safeToBreak(blockC)) return\n        cost += this.exclusionBreak(blockC)\n        toBreak.push(blockC.position)\n      }\n      cost += this.exclusionPlace(blockC)\n      toPlace.push({ x: node.x + dir.x, y: node.y - 1, z: node.z + dir.z, dx: 0, dy: 1, dz: 0 })\n      cost += this.placeCost // additional cost for placing a block\n\n      blockC.height += 1\n    }\n\n    const block0 = this.getBlock(node, 0, -1, 0)\n    if (blockC.height - block0.height > 1.2) return // Too high to jump\n\n    cost += this.safeOrBreak(blockA, toBreak)\n    if (cost > 100) return\n    cost += this.safeOrBreak(blockH, toBreak)\n    if (cost > 100) return\n    cost += this.safeOrBreak(blockB, toBreak)\n    if (cost > 100) return\n\n    neighbors.push(new Move(blockB.position.x, blockB.position.y, blockB.position.z, node.remainingBlocks - toPlace.length, cost, toBreak, toPlace))\n  }\n\n  getMoveForward (node, dir, neighbors) {\n    const blockB = this.getBlock(node, dir.x, 1, dir.z)\n    const blockC = this.getBlock(node, dir.x, 0, dir.z)\n    const blockD = this.getBlock(node, dir.x, -1, dir.z)\n\n    let cost = 1 // move cost\n    cost += this.exclusionStep(blockC)\n\n    const toBreak = []\n    const toPlace = []\n\n    if (!blockD.physical && !blockC.liquid) {\n      if (node.remainingBlocks === 0) return // not enough blocks to place\n\n      if (this.getNumEntitiesAt(blockD.position, 0, 0, 0) > 0) return // D intersects an entity hitbox\n\n      if (!blockD.replaceable) {\n        if (!this.safeToBreak(blockD)) return\n        cost += this.exclusionBreak(blockD)\n        toBreak.push(blockD.position)\n      }\n      cost += this.exclusionPlace(blockD)\n      toPlace.push({ x: node.x, y: node.y - 1, z: node.z, dx: dir.x, dy: 0, dz: dir.z })\n      cost += this.placeCost // additional cost for placing a block\n    }\n\n    cost += this.safeOrBreak(blockB, toBreak)\n    if (cost > 100) return\n\n    // Open fence gates\n    if (this.canOpenDoors && blockC.openable && blockC.shapes && blockC.shapes.length !== 0) {\n      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\n    } else {\n      cost += this.safeOrBreak(blockC, toBreak)\n      if (cost > 100) return\n    }\n\n    if (this.getBlock(node, 0, 0, 0).liquid) cost += this.liquidCost\n\n    neighbors.push(new Move(blockC.position.x, blockC.position.y, blockC.position.z, node.remainingBlocks - toPlace.length, cost, toBreak, toPlace))\n  }\n\n  getMoveDiagonal (node, dir, neighbors) {\n    let cost = Math.SQRT2 // move cost\n    const toBreak = []\n\n    const blockC = this.getBlock(node, dir.x, 0, dir.z) // Landing block or standing on block when jumping up by 1\n    const y = blockC.physical ? 1 : 0\n\n    const block0 = this.getBlock(node, 0, -1, 0)\n\n    let cost1 = 0\n    const toBreak1 = []\n    const blockB1 = this.getBlock(node, 0, y + 1, dir.z)\n    const blockC1 = this.getBlock(node, 0, y, dir.z)\n    const blockD1 = this.getBlock(node, 0, y - 1, dir.z)\n    cost1 += this.safeOrBreak(blockB1, toBreak1)\n    cost1 += this.safeOrBreak(blockC1, toBreak1)\n    if (blockD1.height - block0.height > 1.2) cost1 += this.safeOrBreak(blockD1, toBreak1)\n\n    let cost2 = 0\n    const toBreak2 = []\n    const blockB2 = this.getBlock(node, dir.x, y + 1, 0)\n    const blockC2 = this.getBlock(node, dir.x, y, 0)\n    const blockD2 = this.getBlock(node, dir.x, y - 1, 0)\n    cost2 += this.safeOrBreak(blockB2, toBreak2)\n    cost2 += this.safeOrBreak(blockC2, toBreak2)\n    if (blockD2.height - block0.height > 1.2) cost2 += this.safeOrBreak(blockD2, toBreak2)\n\n    if (cost1 < cost2) {\n      cost += cost1\n      toBreak.push(...toBreak1)\n    } else {\n      cost += cost2\n      toBreak.push(...toBreak2)\n    }\n    if (cost > 100) return\n\n    cost += this.safeOrBreak(this.getBlock(node, dir.x, y, dir.z), toBreak)\n    if (cost > 100) return\n    cost += this.safeOrBreak(this.getBlock(node, dir.x, y + 1, dir.z), toBreak)\n    if (cost > 100) return\n\n    if (this.getBlock(node, 0, 0, 0).liquid) cost += this.liquidCost\n\n    const blockD = this.getBlock(node, dir.x, -1, dir.z)\n    if (y === 1) { // Case jump up by 1\n      if (blockC.height - block0.height > 1.2) return // Too high to jump\n      cost += this.safeOrBreak(this.getBlock(node, 0, 2, 0), toBreak)\n      if (cost > 100) return\n      cost += 1\n      neighbors.push(new Move(blockC.position.x, blockC.position.y + 1, blockC.position.z, node.remainingBlocks, cost, toBreak))\n    } else if (blockD.physical || blockC.liquid) {\n      neighbors.push(new Move(blockC.position.x, blockC.position.y, blockC.position.z, node.remainingBlocks, cost, toBreak))\n    } else if (this.getBlock(node, dir.x, -2, dir.z).physical || blockD.liquid) {\n      if (!blockD.safe) return // don't self-immolate\n      cost += this.getNumEntitiesAt(blockC.position, 0, -1, 0) * this.entityCost\n      neighbors.push(new Move(blockC.position.x, blockC.position.y - 1, blockC.position.z, node.remainingBlocks, cost, toBreak))\n    }\n  }\n\n  getLandingBlock (node, dir) {\n    let blockLand = this.getBlock(node, dir.x, -2, dir.z)\n    while (blockLand.position && blockLand.position.y > this.bot.game.minY) {\n      if (blockLand.liquid && blockLand.safe) return blockLand\n      if (blockLand.physical) {\n        if (node.y - blockLand.position.y <= this.maxDropDown) return this.getBlock(blockLand.position, 0, 1, 0)\n        return null\n      }\n      if (!blockLand.safe) return null\n      blockLand = this.getBlock(blockLand.position, 0, -1, 0)\n    }\n    return null\n  }\n\n  getMoveDropDown (node, dir, neighbors) {\n    const blockB = this.getBlock(node, dir.x, 1, dir.z)\n    const blockC = this.getBlock(node, dir.x, 0, dir.z)\n    const blockD = this.getBlock(node, dir.x, -1, dir.z)\n\n    let cost = 1 // move cost\n    const toBreak = []\n    const toPlace = []\n\n    const blockLand = this.getLandingBlock(node, dir)\n    if (!blockLand) return\n    if (!this.infiniteLiquidDropdownDistance && ((node.y - blockLand.position.y) > this.maxDropDown)) return // Don't drop down into water\n\n    cost += this.safeOrBreak(blockB, toBreak)\n    if (cost > 100) return\n    cost += this.safeOrBreak(blockC, toBreak)\n    if (cost > 100) return\n    cost += this.safeOrBreak(blockD, toBreak)\n    if (cost > 100) return\n\n    if (blockC.liquid) return // dont go underwater\n\n    cost += this.getNumEntitiesAt(blockLand.position, 0, 0, 0) * this.entityCost // add cost for entities\n\n    neighbors.push(new Move(blockLand.position.x, blockLand.position.y, blockLand.position.z, node.remainingBlocks - toPlace.length, cost, toBreak, toPlace))\n  }\n\n  getMoveDown (node, neighbors) {\n    const block0 = this.getBlock(node, 0, -1, 0)\n\n    let cost = 1 // move cost\n    const toBreak = []\n    const toPlace = []\n\n    const blockLand = this.getLandingBlock(node, { x: 0, z: 0 })\n    if (!blockLand) return\n\n    cost += this.safeOrBreak(block0, toBreak)\n    if (cost > 100) return\n\n    if (this.getBlock(node, 0, 0, 0).liquid) return // dont go underwater\n\n    cost += this.getNumEntitiesAt(blockLand.position, 0, 0, 0) * this.entityCost // add cost for entities\n\n    neighbors.push(new Move(blockLand.position.x, blockLand.position.y, blockLand.position.z, node.remainingBlocks - toPlace.length, cost, toBreak, toPlace))\n  }\n\n  getMoveUp (node, neighbors) {\n    const block1 = this.getBlock(node, 0, 0, 0)\n    if (block1.liquid) return\n    if (this.getNumEntitiesAt(node, 0, 0, 0) > 0) return // an entity (besides the player) is blocking the building area\n\n    const block2 = this.getBlock(node, 0, 2, 0)\n\n    let cost = 1 // move cost\n    const toBreak = []\n    const toPlace = []\n    cost += this.safeOrBreak(block2, toBreak)\n    if (cost > 100) return\n\n    if (!block1.climbable) {\n      if (!this.allow1by1towers || node.remainingBlocks === 0) return // not enough blocks to place\n\n      if (!block1.replaceable) {\n        if (!this.safeToBreak(block1)) return\n        toBreak.push(block1.position)\n      }\n\n      const block0 = this.getBlock(node, 0, -1, 0)\n      if (block0.physical && block0.height - node.y < -0.2) return // cannot jump-place from a half block\n\n      cost += this.exclusionPlace(block1)\n      toPlace.push({ x: node.x, y: node.y - 1, z: node.z, dx: 0, dy: 1, dz: 0, jump: true })\n      cost += this.placeCost // additional cost for placing a block\n    }\n\n    if (cost > 100) return\n\n    neighbors.push(new Move(node.x, node.y + 1, node.z, node.remainingBlocks - toPlace.length, cost, toBreak, toPlace))\n  }\n\n  // Jump up, down or forward over a 1 block gap\n  getMoveParkourForward (node, dir, neighbors) {\n    const block0 = this.getBlock(node, 0, -1, 0)\n    const block1 = this.getBlock(node, dir.x, -1, dir.z)\n    if ((block1.physical && block1.height >= block0.height) ||\n      !this.getBlock(node, dir.x, 0, dir.z).safe ||\n      !this.getBlock(node, dir.x, 1, dir.z).safe) return\n    if (this.getBlock(node, 0, 0, 0).liquid) return // cant jump from water\n\n    let cost = 1\n\n    // Leaving entities at the ceiling level (along path) out for now because there are few cases where that will be important\n    cost += this.getNumEntitiesAt(node, dir.x, 0, dir.z) * this.entityCost\n\n    // If we have a block on the ceiling, we cannot jump but we can still fall\n    let ceilingClear = this.getBlock(node, 0, 2, 0).safe && this.getBlock(node, dir.x, 2, dir.z).safe\n\n    // Similarly for the down path\n    let floorCleared = !this.getBlock(node, dir.x, -2, dir.z).physical\n\n    const maxD = this.allowSprinting ? 4 : 2\n\n    for (let d = 2; d <= maxD; d++) {\n      const dx = dir.x * d\n      const dz = dir.z * d\n      const blockA = this.getBlock(node, dx, 2, dz)\n      const blockB = this.getBlock(node, dx, 1, dz)\n      const blockC = this.getBlock(node, dx, 0, dz)\n      const blockD = this.getBlock(node, dx, -1, dz)\n\n      if (blockC.safe) cost += this.getNumEntitiesAt(blockC.position, 0, 0, 0) * this.entityCost\n\n      if (ceilingClear && blockB.safe && blockC.safe && blockD.physical) {\n        cost += this.exclusionStep(blockB)\n        // Forward\n        neighbors.push(new Move(blockC.position.x, blockC.position.y, blockC.position.z, node.remainingBlocks, cost, [], [], true))\n        break\n      } else if (ceilingClear && blockB.safe && blockC.physical) {\n        // Up\n        if (blockA.safe && d !== 4) { // 4 Blocks forward 1 block up is very difficult and fails often\n          cost += this.exclusionStep(blockA)\n          if (blockC.height - block0.height > 1.2) break // Too high to jump\n          cost += this.getNumEntitiesAt(blockB.position, 0, 0, 0) * this.entityCost\n          neighbors.push(new Move(blockB.position.x, blockB.position.y, blockB.position.z, node.remainingBlocks, cost, [], [], true))\n          break\n        }\n      } else if ((ceilingClear || d === 2) && blockB.safe && blockC.safe && blockD.safe && floorCleared) {\n        // Down\n        const blockE = this.getBlock(node, dx, -2, dz)\n        if (blockE.physical) {\n          cost += this.exclusionStep(blockD)\n          cost += this.getNumEntitiesAt(blockD.position, 0, 0, 0) * this.entityCost\n          neighbors.push(new Move(blockD.position.x, blockD.position.y, blockD.position.z, node.remainingBlocks, cost, [], [], true))\n        }\n        floorCleared = floorCleared && !blockE.physical\n      } else if (!blockB.safe || !blockC.safe) {\n        break\n      }\n\n      ceilingClear = ceilingClear && blockA.safe\n    }\n  }\n\n  // for each cardinal direction:\n  // \".\" is head. \"+\" is feet and current location.\n  // \"#\" is initial floor which is always solid. \"a\"-\"u\" are blocks to check\n  //\n  //   --0123-- horizontalOffset\n  //  |\n  // +2  aho\n  // +1  .bip\n  //  0  +cjq\n  // -1  #dkr\n  // -2   els\n  // -3   fmt\n  // -4   gn\n  //  |\n  //  dy\n\n  getNeighbors (node) {\n    const neighbors = []\n\n    // Simple moves in 4 cardinal points\n    for (const i in cardinalDirections) {\n      const dir = cardinalDirections[i]\n      this.getMoveForward(node, dir, neighbors)\n      this.getMoveJumpUp(node, dir, neighbors)\n      this.getMoveDropDown(node, dir, neighbors)\n      if (this.allowParkour) {\n        this.getMoveParkourForward(node, dir, neighbors)\n      }\n    }\n\n    // Diagonals\n    for (const i in diagonalDirections) {\n      const dir = diagonalDirections[i]\n      this.getMoveDiagonal(node, dir, neighbors)\n    }\n\n    this.getMoveDown(node, neighbors)\n    this.getMoveUp(node, neighbors)\n\n    return neighbors\n  }\n}\n\nmodule.exports = Movements\n"
  },
  {
    "path": "lib/passableEntities.json",
    "content": "[\n  \"falling_block\",\n  \"tnt\",\n  \"item\",\n  \"area_effect_cloud\",\n  \"item_frame\",\n  \"leash_knot\",\n  \"painting\",\n  \"arrow\",\n  \"dragon_fireball\",\n  \"fireball\",\n  \"llama_spit\",\n  \"shulker_bullet\",\n  \"small_fireball\",\n  \"snowball\",\n  \"spectral_arrow\",\n  \"egg\",\n  \"ender_pearl\",\n  \"potion\",\n  \"wither_skull\",\n  \"end_crystal\",\n  \"experience_orb\",\n  \"eye_of_ender\",\n  \"firework_rocket\",\n  \"lightning_bolt\",\n  \"experience_bottle\",\n  \"trident\",\n  \"fishing_bobber\",\n  \"evoker_fangs\"\n]"
  },
  {
    "path": "lib/physics.js",
    "content": "const { PlayerState } = require('prismarine-physics')\n\nclass Physics {\n  constructor (bot) {\n    this.bot = bot\n    this.world = { getBlock: (pos) => { return bot.blockAt(pos, false) } }\n  }\n\n  /**\n   *\n   * @param {function} goal A function is the goal has been reached or not\n   * @param {function} controller Controller that can change the current control State for the next tick\n   * @param {number} ticks Number of ticks to simulate\n   * @param {object} state Starting control state to begin the simulation with\n   * @returns { import('prismarine-physics').PlayerState } A player state of the final simulation tick\n   */\n  simulateUntil (goal, controller = () => {}, ticks = 1, state = null) {\n    if (!state) {\n      const simulationControl = {\n        forward: this.bot.controlState.forward,\n        back: this.bot.controlState.back,\n        left: this.bot.controlState.left,\n        right: this.bot.controlState.right,\n        jump: this.bot.controlState.jump,\n        sprint: this.bot.controlState.sprint,\n        sneak: this.bot.controlState.sneak\n      }\n      state = new PlayerState(this.bot, simulationControl)\n    }\n\n    for (let i = 0; i < ticks; i++) {\n      controller(state, i)\n      this.bot.physics.simulatePlayer(state, this.world)\n      if (state.isInLava) return state\n      if (goal(state)) return state\n    }\n\n    return state\n  }\n\n  simulateUntilNextTick () {\n    return this.simulateUntil(() => false, () => {}, 1)\n  }\n\n  simulateUntilOnGround (ticks = 5) {\n    return this.simulateUntil(state => state.onGround, () => {}, ticks)\n  }\n\n  canStraightLine (path, sprint = false) {\n    const reached = this.getReached(path)\n    const state = this.simulateUntil(reached, this.getController(path[0], false, sprint), 200)\n    if (reached(state)) return true\n\n    if (sprint) {\n      if (this.canSprintJump(path, 0)) return false\n    } else {\n      if (this.canWalkJump(path, 0)) return false\n    }\n\n    for (let i = 1; i < 7; i++) {\n      if (sprint) {\n        if (this.canSprintJump(path, i)) return true\n      } else {\n        if (this.canWalkJump(path, i)) return true\n      }\n    }\n    return false\n  }\n\n  canStraightLineBetween (n1, n2) {\n    const reached = (state) => {\n      const delta = n2.minus(state.pos)\n      const r2 = 0.15 * 0.15\n      return (delta.x * delta.x + delta.z * delta.z) <= r2 && Math.abs(delta.y) < 0.001 && (state.onGround || state.isInWater)\n    }\n    const simulationControl = {\n      forward: this.bot.controlState.forward,\n      back: this.bot.controlState.back,\n      left: this.bot.controlState.left,\n      right: this.bot.controlState.right,\n      jump: this.bot.controlState.jump,\n      sprint: this.bot.controlState.sprint,\n      sneak: this.bot.controlState.sneak\n    }\n    const state = new PlayerState(this.bot, simulationControl)\n    state.pos.update(n1)\n    this.simulateUntil(reached, this.getController(n2, false, true), Math.floor(5 * n1.distanceTo(n2)), state)\n    return reached(state)\n  }\n\n  canSprintJump (path, jumpAfter = 0) {\n    const reached = this.getReached(path)\n    const state = this.simulateUntil(reached, this.getController(path[0], true, true, jumpAfter), 20)\n    return reached(state)\n  }\n\n  canWalkJump (path, jumpAfter = 0) {\n    const reached = this.getReached(path)\n    const state = this.simulateUntil(reached, this.getController(path[0], true, false, jumpAfter), 20)\n    return reached(state)\n  }\n\n  getReached (path) {\n    return (state) => {\n      const delta = path[0].minus(state.pos)\n      return Math.abs(delta.x) <= 0.35 && Math.abs(delta.z) <= 0.35 && Math.abs(delta.y) < 1\n    }\n  }\n\n  getController (nextPoint, jump, sprint, jumpAfter = 0) {\n    return (state, tick) => {\n      const dx = nextPoint.x - state.pos.x\n      const dz = nextPoint.z - state.pos.z\n      state.yaw = Math.atan2(-dx, -dz)\n\n      state.control.forward = true\n      state.control.jump = jump && tick >= jumpAfter\n      state.control.sprint = sprint\n    }\n  }\n}\n\nmodule.exports = Physics\n"
  },
  {
    "path": "lib/shapes.js",
    "content": "const { Vec3 } = require('vec3')\n\nfunction getShapeFaceCenters (shapes, direction, half = null) {\n  const faces = []\n  for (const shape of shapes) {\n    const halfsize = new Vec3(shape[3] - shape[0], shape[4] - shape[1], shape[5] - shape[2]).scale(0.5)\n    let center = new Vec3(shape[0] + shape[3], shape[1] + shape[4], shape[2] + shape[5]).scale(0.5)\n    center = center.offset(halfsize.x * direction.x, halfsize.y * direction.y, halfsize.z * direction.z)\n\n    if (half === 'top' && center.y <= 0.5) {\n      if (Math.abs(direction.y) === 0) center.y += halfsize.y - 0.001\n      if (center.y <= 0.5) continue\n    } else if (half === 'bottom' && center.y >= 0.5) {\n      if (Math.abs(direction.y) === 0) center.y -= halfsize.y - 0.001\n      if (center.y >= 0.5) continue\n    }\n\n    faces.push(center)\n  }\n  return faces\n}\n\nmodule.exports = { getShapeFaceCenters }\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"mineflayer-pathfinder\",\n  \"version\": \"2.4.5\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"mocha_test\": \"mocha --reporter spec --exit\",\n    \"mocha_debug\": \"mocha --inspect-brk --reporter spec --exit\",\n    \"lint\": \"standard\",\n    \"fix\": \"standard --fix\",\n    \"test\": \"npm run lint && npm run mocha_test\"\n  },\n  \"author\": \"Karang\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"minecraft-data\": \"^3.5.1\",\n    \"prismarine-block\": \"^1.16.3\",\n    \"prismarine-entity\": \"^2.1.1\",\n    \"prismarine-item\": \"^1.11.5\",\n    \"prismarine-nbt\": \"^2.2.1\",\n    \"prismarine-physics\": \"^1.5.2\",\n    \"vec3\": \"^0.2.0\"\n  },\n  \"devDependencies\": {\n    \"minecraft-wrap\": \"^1.2.1\",\n    \"mineflayer\": \"^4.3.0\",\n    \"mineflayer-pathfinder\": \"file:./\",\n    \"mocha\": \"^11.0.1\",\n    \"prismarine-schematic\": \"^1.2.3\",\n    \"standard\": \"^17.0.0\"\n  }\n}\n"
  },
  {
    "path": "readme.md",
    "content": "# Mineflayer-pathfinder\n\n[![npm version](https://badge.fury.io/js/mineflayer-pathfinder.svg)](https://badge.fury.io/js/mineflayer-pathfinder) ![npm](https://img.shields.io/npm/dt/mineflayer-pathfinder) [![Try it on gitpod](https://img.shields.io/badge/try-on%20gitpod-brightgreen.svg)](https://gitpod.io/#https://github.com/PrismarineJS/mineflayer-pathfinder) [![Issue Hunt](https://github.com/BoostIO/issuehunt-materials/blob/master/v1/issuehunt-shield-v1.svg)](https://issuehunt.io/r/PrismarineJS/mineflayer-pathfinder)\n\nPathfinding plugin for the Minecraft Bot API [Mineflayer](https://github.com/PrismarineJS/mineflayer). Create static, dynamic or composite goals to navigate Minecraft terrain fully autonomously.\n\nMostly stable. Feel free to contribute by making suggestions or posting issues.\n\n## Install\n\n```bash\nnpm install mineflayer-pathfinder\n```\n\n## Tutorial & Explanation\n\nFor a basic explanation of how to use mineflayer-pathfinder, you can read [this tutorial](./examples/tutorial/goalsExplained.md).\n\n## Video Tutorials\n\nFor a video tutorial explaining the usage of mineflayer-pathfinder, you can watch the following Youtube videos:\n\n[<img src=\"https://img.youtube.com/vi/UWGSf08wQSc/0.jpg\" alt=\"part 1\" width=\"200\">](https://www.youtube.com/watch?v=UWGSf08wQSc)\n[<img src=\"https://img.youtube.com/vi/ssWE0kXDGJE/0.jpg\" alt=\"part 2\" width=\"200\">](https://www.youtube.com/watch?v=ssWE0kXDGJE)\n\n## Example\n\n```js\nconst mineflayer = require('mineflayer')\nconst pathfinder = require('mineflayer-pathfinder').pathfinder\nconst Movements = require('mineflayer-pathfinder').Movements\nconst { GoalNear } = require('mineflayer-pathfinder').goals\nconst bot = mineflayer.createBot({ username: 'Player' })\n\nbot.loadPlugin(pathfinder)\n\nbot.once('spawn', () => {\n  const defaultMove = new Movements(bot)\n  \n  bot.on('chat', function(username, message) {\n  \n    if (username === bot.username) return\n\n    const target = bot.players[username] ? bot.players[username].entity : null\n    if (message === 'come') {\n      if (!target) {\n        bot.chat('I don\\'t see you !')\n        return\n      }\n      const p = target.position\n\n      bot.pathfinder.setMovements(defaultMove)\n      bot.pathfinder.setGoal(new GoalNear(p.x, p.y, p.z, 1))\n    } \n  })\n})\n```\n\n## Features\n * Optimized and modernized A* pathfinding\n * Complexe goals can be specified (inspired by [baritone goals](https://github.com/cabaletta/baritone/blob/master/FEATURES.md#goals) )\n * Customizable movements generator\n * Each movement can have a different cost\n * Can break/place blocks as part of its deplacement\n * Automatically update path when environment change\n * Long distance paths\n * Can swim\n * Can avoid entities\n * Modular and easily extendable with different behavior\n\n## API\nConsidering there are a lot of deep changes that are being worked on, it could take some time before it's done\n\nAlso, **for now**, there is only the `pathfinder` module, `movements` and `goals` still need to be done\n\n\n# Functions:\n\n### bot.pathfinder.goto(goal)\nReturns a Promise with the path result. Resolves when the goal is reached. Rejects on error.\n * `goal` - Goal instance\n\n### bot.pathfinder.bestHarvestTool(block)\nReturns the best harvesting tool in the inventory for the specified block.\n * `Returns` - `Item` instance or `null`\n * `block` - Block instance\n\n### bot.pathfinder.getPathTo(movements, goal, timeout)\n * `Returns` - The path\n * `movements` - Movements instance\n * `goal` - Goal instance\n * `timeout` - number (optional, default `bot.pathfinder.thinkTimeout`)\n\n### bot.pathfinder.getPathFromTo* (movements, startPos, goal, options = {})\nReturns a Generator. The generator computes the path for as longs as no full path is found or `options.timeout` is reached. \nThe generator will block the event loop until a path is found or `options.tickTimeout` (default to 50ms) is reached.\n * `Returns` - A generator instance. See [MDN function*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*).\n * `movements` - Movements instance\n * `startPos` - A Vec3 instance. The starting position to base the path search from. \n * `goal` - Goal instance\n * `options` - A optional options object contains:\n   * `optimizePath` - Boolean Optional. Optimize path for shortcuts like going to the next node in a strait line instead walking only diagonal or along axis.\n   * `resetEntityIntersects` - Boolean Optional. Reset the `entityIntersections` index for `movements`. Default: true\n   * `timeout` - Number Optional. Total computation timeout.\n   * `tickTimeout` - Number Optional. Maximum amount off time before yielding.\n   * `searchRadius` - Number Optional. Max distance to search.\n   * `startMove` - instance of Move Optional. A optional starting position as a Move. Replaces `startPos` as the starting position.\n\n### bot.pathfinder.setGoal(Goal, dynamic)\n * `goal` - Goal instance\n * `dynamic` - boolean (optional, default false)\n \n### bot.pathfinder.setMovements(movements)\nAssigns the movements config.\n * `movements` - Movements instance\n\n### bot.pathfinder.stop()\nStops pathfinding as soon as the bot has reached the next node in the path (this prevents the bot from stopping mid-air). Emits `path_stop` when called.\nNote: to force stop immediately, use `bot.pathfinder.setGoal(null)`\n\n### bot.pathfinder.isMoving()\nA function that checks if the bot is currently moving.\n * `Returns` - boolean\n\n### bot.pathfinder.isMining()\nA function that checks if the bot is currently mining blocks.\n * `Returns` - boolean\n\n### bot.pathfinder.isBuilding()\nA function that checks if the bot is currently placing blocks.\n * `Returns` - boolean\n\n# Properties:\n### bot.pathfinder.thinkTimeout\nThink Timeout in milliseconds.\n * `Default` - `5000`\n\n### bot.pathfinder.tickTimeout\nHow many milliseconds per tick are allocated to thinking.\n * `Default` - `40`\n\n### bot.pathfinder.searchRadius\nThe search limiting radius, in blocks, if `-1` the search is not limited by distance.\n * `Default` - `-1`\n\n# Movement class\nThis class configures how pathfinder plans its paths. It configures things like block breaking or different costs for moves. This class can be extended to add or change how pathfinder calculates its moves.\n\n## Usage\nPathfinder instantiates the default movement class by itself if no instance is specified. If you want to change values you should create a new instance of the Movements class, change it's values and set it as pathfinders new movement class. \n### Example:\n```js\nconst { Movements } = require('mineflayer-pathfinder') // Import the Movements class from pathfinder\n\nbot.once('spawn', () => {\n  // A new movement instance for specific behavior\n  const defaultMove = new Movements(bot)\n\n  defaultMove.allow1by1towers = false // Do not build 1x1 towers when going up\n  defaultMove.canDig = false // Disable breaking of blocks when pathing \n  defaultMove.scafoldingBlocks.push(bot.registry.itemsByName['netherrack'].id) // Add nether rack to allowed scaffolding items\n  bot.pathfinder.setMovements(defaultMove) // Update the movement instance pathfinder uses\n\n  // Do pathfinder things\n  // ...\n})\n```\n\n## Movements class default properties\nMovement class properties and their default values.\n### canDig\nBoolean to allow breaking blocks.\n* Default `true`\n\n### digCost\nAdditional cost for breaking blocks.\n* Default - `1`\n\n### placeCost\nAdditional cost for placing blocks.\n* Default - `1`\n\n### maxDropDown\nMax drop down distance. Only considers drops that have blocks to land on.\n* Default - `4`\n\n### infiniteLiquidDropdownDistance\nOption to ignore maxDropDown distance when the landing position is in water.\n* Default - `true`\n\n### liquidCost\nAdditional cost for interacting with liquids.\n* Default - `1`\n\n### entityCost\nAdditional cost for moving through an entity hitbox (besides passable ones).\n* Default - `1`\n\n### dontCreateFlow\nDo not break blocks that touch liquid blocks.\n* Default - `true`\n\n### dontMineUnderFallingBlock\nDo not break blocks that have a gravityBlock above.\n* Default - `true`\n\n### allow1by1towers\nAllow pillaring up on 1x1 towers.\n* Default - `true`\n\n### allowFreeMotion\nAllow to walk to the next node/goal in a straight line if terrain allows it.\n* Default - `false`\n\n### allowParkour\nAllow parkour jumps like jumps over gaps bigger then 1 block.\n* Default - `true`\n\n### allowSprinting\nAllow sprinting when moving.\n* Default - `true`\n\n### allowEntityDetection\nTest for entities that may obstruct path or prevent block placement. Grabs updated entities every new path.\n* Default - `true`\n\n### entitiesToAvoid\nSet of entities (by bot.registry name) to completely avoid when using entity detection.\n* instance of `Set`\n\n### passableEntities\nSet of entities (by bot.registry name) to ignore when using entity detection.\n* instance of `Set`\n* Default - See lib/passableEntities.json\n\n### interactableBlocks\nSet of blocks (by bot.registry name) that pathfinder should not attempt to place blocks or 'right click' on.\n* instance of `Set`\n* Default - See lib/interactable.json\n\n### blocksCantBreak\nSet of block id's pathfinder cannot break. Includes chests and all unbreakable blocks.\n* instance of `Set`\n\n### blocksToAvoid\nSet of block id's to avoid.\n* instance of `Set`\n\n### liquids\nSet of liquid block id's.\n* instance of `Set`\n\n### climbables\nSet of block id's that are climable. Note: Currently unused as pathfinder cannot use climables.\n* instance of `Set`\n\n### replaceables\nSet of block id's that can be replaced when placing blocks.\n* instance of `Set`\n\n### scafoldingBlocks\nArray of item id's that can be used as scaffolding blocks.\n* Default - `[<scaffoldingItems>]`\n\n### gravityBlocks\nSet of block id's that can fall on bot's head.\n* instance of `Set`\n\n### fences\nSet of block id's that are fences or blocks that have a collision box taller then 1 block.\n* instance of `Set`\n\n### carpets\nSet of all carpet block id's or blocks that have a collision box smaller then 0.1. These blocks are considered safe to walk in.\n* instance of `Set`\n\n### exclusionAreasStep\nAn array of functions that define an area or block to be step on excluded. Every function in the array is parsed the Block the bot is planing to step on. Each function should return a positive number (includes 0) that defines extra cost for that specific Block. 0 means no extra cost, 100 means it is impossible for pathfinder to consider this move.\n* Array of functions `(block: Block) => number`\n\n### exclusionAreasBreak\nAn array of functions that define an area or block to be break excluded. Every function in the array is parsed the Block the bot is planing to break. Each function should return a positive number (includes 0) that defines extra cost for that specific Block. 0 means no extra cost, 100 means it is impossible for pathfinder to consider this move.\n* Array of functions `(block: Block) => number`\n\n### exclusionAreasPlace\nAn array of functions that define an area to be block placement excluded. Every function in the array is parsed the current Block the bot is planing to place a block inside (should be air or a replaceable block most of the time). Each function should return a positive number (includes 0) that defines extra cost for that specific Block. 0 means no extra cost, 100 makes it impossible for pathfinder to consider this move.\n* Array of functions `(block: Block) => number`\n\n### entityIntersections\nA dictionary of the number of entities intersecting each floored block coordinate. Updated automatically for each path, but you may mix in your own entries before calculating a path if desired (generally for testing). To prevent this from being cleared automatically before generating a path,s see the [path gen options](#botpathfindergetpathfromto-movements-startpos-goal-options--). \n* Formatted entityIntersections['x,y,z'] = #ents\n* Dictionary of costs `{string: number}`\n\n### canOpenDoors\nEnable feature to open Fence Gates. Unreliable and known to be buggy.\n* Default - `false`\n\n# Events:\n\n### goal_reached\nCalled when the goal has been reached. Not called for dynamic goals.\n\n### path_update\nCalled whenever the path is recalculated. Status may be:\n * `success` a path has been found\n * `partial` a partial path has been found, computations will continue next tick\n * `timeout` timed out\n * `noPath` no path was found\n\n### goal_updated\nCalled whenever a new goal is assigned to the pathfinder.\n\n### path_reset\nCalled when the path is reset, with a reason:\n * `goal_updated`\n * `movements_updated`\n * `block_updated`\n * `chunk_loaded`\n * `goal_moved`\n * `dig_error`\n * `no_scaffolding_blocks`\n * `place_error`\n * `stuck`\n\n ### path_stop\n Called when the pathing has been stopped by `bot.pathfinder.stop()`\n\n# Goals:\n\n### Goal\nAbstract Goal class. Do not instantiate this class. Instead extend it to make a new Goal class.\n\nHas abstract methods:\n - `heuristic(node)`\n   * `node` - A path node\n   * Returns a heuristic number value for a given node. Must be admissible – meaning that it never overestimates the actual cost to get to the goal.\n - `isEnd(node)`\n   * `node`\n   * Returns a boolean value if the given node is a end node. \n\nImplements default methods for:\n - `isValid()`\n   * Always returns `true`\n - `hasChanged(node)`\n   * `node` - A path node\n   * Always returns `false`\n\n### GoalBlock(x, y, z)\nOne specific block that the player should stand inside at foot level\n * `x` - Integer\n * `y` - Integer\n * `z` - Integer\n\n### GoalNear(x, y, z, range)\nA block position that the player should get within a certain radius of\n * `x` - Integer\n * `y` - Integer\n * `z` - Integer\n * `range` - Integer\n \n### GoalXZ(x, z)\nUseful for long-range goals that don't have a specific Y level\n * `x` - Integer\n * `z` - Integer\n\n### GoalNearXZ(x, z, range)\nUseful for finding builds that you don't have an exact Y level for, just an approximate X and Z level.\n * `x` - Integer\n * `z` - Integer\n * `range` - Integer\n\n### GoalY(y)\nGet to a Y level.\n * `y` - Integer\n\n\n### GoalGetToBlock(x, y, z)\nDon't get into the block, but get directly adjacent to it. Useful for chests.\n * `x` - Integer\n * `y` - Integer\n * `z` - Integer\n\n### GoalCompositeAny(Array\\<Goal>?)\nA composite of many goals, any one of which satisfies the composite.\nFor example, a GoalCompositeAny of block goals for every oak log in loaded\nchunks would result in it pathing to the easiest oak log to get to.\n * `Array` - Array of goals\n\n### GoalCompositeAll(Array\\<Goal>?)\nA composite of multiple goals, requiring all of them to be satisfied.\n * `Array` - Array of goals\n\n### GoalInvert(goal)\nInverts the goal.\n * `goal` - Goal to invert\n\n### GoalFollow(entity, range)\nFollows an entity.\n * `entity` - Entity instance\n * `range` - Integer\n\n### GoalPlaceBlock(pos, world, options)\nPosition the bot in order to place a block.\n * `pos` - Vec3 the position of the placed block\n * `world` - the world of the bot (Can be accessed with `bot.world`)\n * `options` - object containing all optionals properties:\n   * `range` - maximum distance from the clicked face\n   * `faces` - the directions of the faces the player can click\n   * `facing` - the direction the player must be facing\n   * `facing3D` - boolean, facing is 3D (true) or 2D (false)\n   * `half` - `top` or `bottom`, the half that must be clicked\n\n ### GoalLookAtBlock(pos, world, options = {})\n Path into a position were a blockface of block at pos is visible. Fourth argument is optional and contains extra options.\n  * `pos` - Vec3 the block position to look at\n  * `world` - the world of the bot (Can be accessed with `bot.world`)\n  * `options` - object containing all optionals properties:\n    * `reach` - number maximum distance from the clicked face. Default `4.5`\n    * `entityHeight` - number Default is `1.6`\n\n ### GoalBreakBlock(x, y, z, bot, options)\n Deprecated. Wrapper for GoalLookAtBlock. Use GoalLookAtBlock instead.\n"
  },
  {
    "path": "test/internalTest.js",
    "content": "/* eslint-env mocha */\n\nconst mineflayer = require('mineflayer')\nconst { goals, pathfinder, Movements } = require('mineflayer-pathfinder')\nconst { Vec3 } = require('vec3')\nconst mc = require('minecraft-protocol')\nconst assert = require('assert')\nconst { v4: uuidv4 } = require('uuid')\nconst PEntity = require('prismarine-entity')\nconst { once, on } = require('events')\nconst { Schematic } = require('prismarine-schematic')\nconst { promises: fs } = require('fs')\nconst path = require('path')\nconst Physics = require('../lib/physics')\n\nconst Version = '1.16.5'\nconst ServerPort = 25567\n\n/**\n * Returns a flat bedrock chunk with a single gold block in it.\n * @param {string} Version version\n * @returns {import('prismarine-chunk').Chunk}\n */\nfunction flatMap (Version) {\n  const targetBlock = new Vec3(12, 1, 8) // a gold block away from the spawn position\n  const Block = require('prismarine-block')(Version)\n  const Chunk = require('prismarine-chunk')(Version)\n  const mcData = require('minecraft-data')(Version)\n  const chunk = new Chunk()\n  chunk.initialize((x, y, z) => {\n    if (targetBlock.x === x && targetBlock.y === y && targetBlock.z === z) {\n      return new Block(mcData.blocksByName.gold_block.id, 1, 0)\n    }\n    return y === 0 ? new Block(mcData.blocksByName.bedrock.id, 1, 0) : new Block(mcData.blocksByName.air.id, 1, 0) // Bedrock floor\n  })\n  return chunk\n}\n\n/**\n * Reads the schematic parkour1.schem and returns a chunk containing the schematic content.\n * @param {string} Version version to be used\n * @returns {Promise<import('prismarine-chunk').Chunk>}\n */\nasync function parkourMap (Version) {\n  const pwd = path.join(__dirname, './schematics/parkour1.schem')\n  const readSchem = await Schematic.read(await fs.readFile(pwd), '1.18.2')\n  const Block = require('prismarine-block')(Version)\n  const Chunk = require('prismarine-chunk')(Version)\n  const mcData = require('minecraft-data')(Version)\n  const chunk = new Chunk()\n  chunk.initialize((x, y, z) => {\n    const block = readSchem.getBlock(new Vec3(x, y, z))\n    if (block.name === 'air') return null\n    // Different versions off schematic are not compatible with each other. Assumes block names between versions stay the same.\n    const blockVersion = mcData.blocksByName[block.name]\n    if (!blockVersion) return null\n    return new Block(blockVersion.id, 1, 0)\n  })\n  return chunk\n}\n\nfunction generateChunkPacket (chunk) {\n  const lights = chunk.dumpLight()\n  return {\n    x: 0,\n    z: 0,\n    groundUp: true,\n    biomes: chunk.dumpBiomes !== undefined ? chunk.dumpBiomes() : undefined,\n    heightmaps: {\n      type: 'compound',\n      name: '',\n      value: {\n        MOTION_BLOCKING: { type: 'longArray', value: new Array(36).fill([0, 0]) }\n      }\n    }, // send fake heightmap\n    bitMap: chunk.getMask(),\n    chunkData: chunk.dump(),\n    blockEntities: [],\n    trustEdges: false,\n    skyLightMask: lights?.skyLightMask,\n    blockLightMask: lights?.blockLightMask,\n    emptySkyLightMask: lights?.emptySkyLightMask,\n    emptyBlockLightMask: lights?.emptyBlockLightMask,\n    skyLight: lights?.skyLight,\n    blockLight: lights?.blockLight\n  }\n}\n\n/**\n * Create a new 1.16 server and handle when clients connect.\n * @param {import('minecraft-protocol').Server} server\n * @param {import('vec3').Vec3} spawnPos\n * @param {string} Version\n * @param {boolean} useLoginPacket\n * @returns {Promise<void>}\n */\nasync function newServer (server, chunk, spawnPos, Version, useLoginPacket) {\n  const mcData = require('minecraft-data')(Version)\n  server = mc.createServer({\n    'online-mode': false,\n    version: Version,\n    // 25565 - local server, 25566 - proxy server\n    port: ServerPort\n  })\n  server.on('login', (client) => {\n    let loginPacket\n    if (useLoginPacket) {\n      loginPacket = mcData.loginPacket\n    } else {\n      loginPacket = {\n        entityId: 0,\n        levelType: 'fogetaboutit',\n        gameMode: 0,\n        previousGameMode: 255,\n        worldNames: ['minecraft:overworld'],\n        dimension: 0,\n        worldName: 'minecraft:overworld',\n        hashedSeed: [0, 0],\n        difficulty: 0,\n        maxPlayers: 20,\n        reducedDebugInfo: 1,\n        enableRespawnScreen: true\n      }\n    }\n\n    client.write('login', loginPacket)\n\n    client.write('map_chunk', generateChunkPacket(chunk))\n\n    client.write('position', {\n      x: spawnPos.x,\n      y: spawnPos.y,\n      z: spawnPos.z,\n      yaw: 0,\n      pitch: 0,\n      flags: 0x00\n    })\n  })\n\n  await once(server, 'listening')\n  return server\n}\n\nfunction add1x2Weight (entityIntersections, posX, posY, posZ, weight = 1) {\n  entityIntersections[`${posX},${posY},${posZ}`] = entityIntersections[`${posX},${posY},${posZ}`] ?? 0\n  entityIntersections[`${posX},${posY + 1},${posZ}`] = entityIntersections[`${posX},${posY + 1},${posZ}`] ?? 0\n\n  entityIntersections[`${posX},${posY},${posZ}`] += weight\n  entityIntersections[`${posX},${posY + 1},${posZ}`] += weight\n}\n\ndescribe('pathfinder Goals', function () {\n  const mcData = require('minecraft-data')(Version)\n\n  const targetBlock = new Vec3(12, 1, 8) // a gold block away from the spawn position\n  const spawnPos = new Vec3(8.5, 1, 8.5) // Center of the chunk & center of the block\n\n  /** @type { import('mineflayer').Bot & { pathfinder: import('mineflayer-pathfinder').Pathfinder }} */\n  let bot\n  /** @type { import('minecraft-protocol').Server } */\n  let server\n\n  before(async () => {\n    const chunk = flatMap(Version)\n    server = await newServer(server, chunk, spawnPos, Version, true)\n    bot = mineflayer.createBot({\n      username: 'player',\n      version: Version,\n      port: ServerPort\n    })\n    await once(bot, 'chunkColumnLoad')\n  })\n  after(() => {\n    bot.end()\n    bot = null\n    server.close()\n  })\n\n  describe('Goals', () => {\n    beforeEach(() => {\n      bot.entity.position = spawnPos.clone()\n    })\n\n    it('GoalBlock', () => {\n      const goal = new goals.GoalBlock(targetBlock.x, targetBlock.y, targetBlock.z)\n      assert.ok(!goal.isEnd(bot.entity.position))\n      bot.entity.position = targetBlock.clone()\n      assert.ok(goal.isEnd(bot.entity.position))\n    })\n\n    it('GoalNear', () => {\n      const goal = new goals.GoalNear(targetBlock.x, targetBlock.y, targetBlock.z, 1)\n      assert.ok(!goal.isEnd(bot.entity.position))\n      bot.entity.position = targetBlock.offset(1, 0, 0)\n      assert.ok(goal.isEnd(bot.entity.position))\n    })\n\n    it('GoalXZ', () => {\n      const goal = new goals.GoalXZ(targetBlock.x, targetBlock.z)\n      assert.ok(!goal.isEnd(bot.entity.position))\n      bot.entity.position = targetBlock.offset(0, 1, 0)\n      assert.ok(goal.isEnd(bot.entity.position))\n    })\n\n    it('GoalNearXZ', () => {\n      const goal = new goals.GoalNearXZ(targetBlock.x, targetBlock.z, 1)\n      assert.ok(!goal.isEnd(bot.entity.position))\n      bot.entity.position = targetBlock.offset(1, 0, 0)\n      assert.ok(goal.isEnd(bot.entity.position))\n    })\n\n    it('GoalY', () => {\n      const goal = new goals.GoalY(targetBlock.y + 1)\n      assert.ok(!goal.isEnd(bot.entity.position))\n      bot.entity.position = targetBlock.offset(0, 1, 0)\n      assert.ok(goal.isEnd(bot.entity.position))\n    })\n\n    it('GoalGetToBlock', () => {\n      const goal = new goals.GoalGetToBlock(targetBlock.x, targetBlock.y, targetBlock.z)\n      assert.ok(!goal.isEnd(bot.entity.position))\n      bot.entity.position = targetBlock.offset(1, 0, 0)\n      assert.ok(goal.isEnd(bot.entity.position))\n    })\n\n    it('GoalCompositeAny', () => {\n      const targetBlock2 = new Vec3(10, 1, 0)\n      const goal1 = new goals.GoalBlock(targetBlock.x, targetBlock.y, targetBlock.z)\n      const goal2 = new goals.GoalBlock(targetBlock2.x, targetBlock2.y, targetBlock2.z)\n      const goalComposite = new goals.GoalCompositeAny()\n      goalComposite.goals = [goal1, goal2]\n      assert.ok(!goalComposite.isEnd(bot.entity.position))\n      bot.entity.position = targetBlock.clone()\n      assert.ok(goalComposite.isEnd(bot.entity.position)) // target block 1\n      bot.entity.position = targetBlock2.clone()\n      assert.ok(goalComposite.isEnd(bot.entity.position)) // target block 2\n    })\n\n    it('GoalCompositeAll', () => {\n      const targetBlock = new Vec3(2, 1, 0)\n      const block2 = new Vec3(3, 1, 0)\n      const goal1 = new goals.GoalBlock(targetBlock.x, targetBlock.y, targetBlock.z)\n      const goal2 = new goals.GoalNear(block2.x, block2.y, block2.z, 2)\n      const goalComposite = new goals.GoalCompositeAll()\n      goalComposite.goals = [goal1, goal2]\n      assert.ok(!goalComposite.isEnd(bot.entity.position))\n      bot.entity.position = targetBlock.offset(0, 0, 0)\n      assert.ok(goalComposite.isEnd(bot.entity.position))\n    })\n\n    it('GoalInvert', () => {\n      const goalBlock = new goals.GoalBlock(targetBlock.x, targetBlock.y, targetBlock.z)\n      const goal = new goals.GoalInvert(goalBlock)\n      bot.entity.position = targetBlock.clone()\n      assert.ok(!goal.isEnd(bot.entity.position))\n      bot.entity.position = new Vec3(0, 1, 0)\n      assert.ok(goal.isEnd(bot.entity.position))\n    })\n\n    it('GoalPlaceBlock', () => {\n      const placeTarget = targetBlock.offset(0, 1, 0)\n      const goal = new goals.GoalPlaceBlock(placeTarget, bot.world, {})\n      bot.entity.position = targetBlock.offset(-5, 0, 0) // to far away to reach\n      assert.ok(!goal.isEnd(bot.entity.position.floored()))\n      bot.entity.position = targetBlock.offset(-2, 0, 0)\n      assert.ok(goal.isEnd(bot.entity.position.floored()))\n    })\n\n    it('GoalLookAtBlock', () => {\n      const breakTarget = targetBlock.clone() // should be a gold block or any other block thats dig able\n      const goal = new goals.GoalLookAtBlock(breakTarget, bot.world, { reach: 3 })\n      assert.ok(!goal.isEnd(bot.entity.position.floored()))\n      bot.entity.position = targetBlock.offset(-2, 0, 0) // should now be close enough\n      assert.ok(goal.isEnd(bot.entity.position.floored()))\n    })\n  })\n\n  describe('Goals with entity', () => {\n    beforeEach(() => {\n      bot.entity.position = spawnPos.clone()\n    })\n    before((done) => {\n      const Entity = PEntity(Version)\n      const chicken = new Entity(mcData.entitiesByName.chicken.id)\n      const client = Object.values(server.clients)[0]\n      client.write('spawn_entity', { // Might only work for 1.16\n        entityId: chicken.id,\n        objectUUID: uuidv4(),\n        type: chicken.type,\n        x: targetBlock.x,\n        y: targetBlock.y + 1,\n        z: targetBlock.z,\n        pitch: 0,\n        yaw: 0,\n        objectData: 0,\n        velocityX: 0,\n        velocityY: 0,\n        velocityZ: 0\n      })\n      setTimeout(done, 100)\n    })\n\n    it('GoalFollow', () => {\n      const entity = bot.nearestEntity() || { position: targetBlock.offset(0, 1, 0) }\n      const goal = new goals.GoalFollow(entity, 1)\n      assert.ok(!goal.isEnd(bot.entity.position))\n      bot.entity.position = targetBlock.clone()\n      assert.ok(goal.isEnd(bot.entity.position))\n    })\n  })\n})\n\ndescribe('pathfinder events', function () {\n  const mcData = require('minecraft-data')(Version)\n\n  const targetBlock = new Vec3(12, 1, 8) // a gold block away from the spawn position\n  const spawnPos = new Vec3(8.5, 1, 8.5) // Center of the chunk & center of the block\n\n  /** @type { import('mineflayer').Bot & { pathfinder: import('mineflayer-pathfinder').Pathfinder }} */\n  let bot\n  /** @type { import('minecraft-protocol').Server } */\n  let server\n\n  before(async () => {\n    const chunk = flatMap(Version)\n    server = await newServer(server, chunk, spawnPos, Version, true)\n    bot = mineflayer.createBot({\n      username: 'player',\n      version: Version,\n      port: ServerPort\n    })\n    await once(bot, 'chunkColumnLoad')\n    bot.loadPlugin(pathfinder)\n    bot.pathfinder.setMovements(new Movements(bot, mcData))\n  })\n  after(() => server.close())\n\n  describe('events', async function () {\n    beforeEach(() => {\n      bot.entity.position = spawnPos.clone()\n    })\n    afterEach((done) => {\n      bot.pathfinder.setGoal(null)\n      setTimeout(done)\n      const listeners = ['goal_reached', 'goal_updated', 'path_update', 'path_stop']\n      listeners.forEach(l => bot.removeAllListeners(l))\n    })\n\n    it('goal_reached', function (done) {\n      this.timeout(3000)\n      this.slow(1000)\n      bot.once('goal_reached', () => done())\n      bot.pathfinder.setGoal(new goals.GoalNear(targetBlock.x, targetBlock.y, targetBlock.z, 1))\n    })\n\n    it('goal_updated', function (done) {\n      this.timeout(100)\n      bot.once('goal_updated', () => done())\n      bot.pathfinder.setGoal(new goals.GoalNear(targetBlock.x, targetBlock.y, targetBlock.z, 1))\n    })\n\n    it('path_update', function (done) {\n      this.timeout(3000)\n      this.slow(1000)\n      bot.pathfinder.setGoal(new goals.GoalNear(targetBlock.x, targetBlock.y, targetBlock.z, 1))\n      bot.once('path_update', () => done())\n    })\n\n    it('path_stop', function (done) {\n      this.timeout(3000)\n      this.slow(1000)\n      bot.pathfinder.setGoal(new goals.GoalNear(targetBlock.x, targetBlock.y, targetBlock.z, 1))\n      bot.once('path_stop', () => done())\n      bot.pathfinder.stop()\n    })\n  })\n})\n\ndescribe('pathfinder util functions', function () {\n  const mcData = require('minecraft-data')(Version)\n  const Item = require('prismarine-item')(Version)\n\n  const targetBlock = new Vec3(12, 1, 8) // a gold block away from the spawn position\n  const spawnPos = new Vec3(8.5, 1, 8.5) // Center of the chunk & center of the block\n\n  const itemsToGive = [new Item(mcData.itemsByName.diamond_pickaxe.id, 1), new Item(mcData.itemsByName.dirt.id, 64)]\n\n  /** @type { import('mineflayer').Bot & { pathfinder: import('mineflayer-pathfinder').Pathfinder }} */\n  let bot\n  /** @type { import('minecraft-protocol').Server } */\n  let server\n\n  before(async () => {\n    const chunk = flatMap(Version)\n    server = await newServer(server, chunk, spawnPos, Version, true)\n    bot = mineflayer.createBot({\n      username: 'player',\n      version: Version,\n      port: ServerPort\n    })\n    await once(bot, 'chunkColumnLoad')\n    itemsToGive.forEach(item => {\n      const slot = bot.inventory.firstEmptyHotbarSlot()\n      bot.inventory.slots[slot] = item\n    })\n    bot.loadPlugin(pathfinder)\n    bot.pathfinder.setMovements(new Movements(bot, mcData))\n  })\n  after(() => server.close())\n\n  describe('paththing', function () {\n    this.afterEach((done) => {\n      bot.pathfinder.setGoal(null)\n      bot.entity.position = spawnPos.clone()\n      bot.stopDigging()\n      setTimeout(() => done())\n    })\n\n    it('Goto', async function () {\n      this.timeout(3000)\n      this.slow(1500)\n      await bot.pathfinder.goto(new goals.GoalGetToBlock(targetBlock.x, targetBlock.y, targetBlock.z))\n    })\n\n    it('isMoving', function (done) {\n      bot.pathfinder.setGoal(new goals.GoalGetToBlock(targetBlock.x, targetBlock.y, targetBlock.z))\n      const foo = () => {\n        if (bot.pathfinder.isMoving()) {\n          bot.removeListener('physicTick', foo)\n          done()\n        }\n      }\n      bot.on('physicTick', foo)\n    })\n\n    // Note: Ordering seams to matter when running the isBuilding test. If run after isMining isBuilding does not seam to work.\n    it('isBuilding', function (done) {\n      this.timeout(5000)\n      this.slow(1500)\n\n      bot.pathfinder.setGoal(new goals.GoalBlock(targetBlock.x, targetBlock.y + 2, targetBlock.z))\n      const foo = () => {\n        if (bot.pathfinder.isBuilding()) {\n          bot.removeListener('physicTick', foo)\n          bot.stopDigging()\n          done()\n        }\n      }\n      bot.on('physicTick', foo)\n    })\n\n    it('isMining', function (done) {\n      this.timeout(5000)\n      this.slow(1500)\n\n      bot.pathfinder.setGoal(new goals.GoalBlock(targetBlock.x, targetBlock.y, targetBlock.z))\n      const foo = () => {\n        if (bot.pathfinder.isMining()) {\n          bot.removeListener('physicTick', foo)\n          bot.stopDigging()\n          done()\n        }\n      }\n      bot.on('physicTick', foo)\n    })\n  })\n\n  it('bestHarvestTool', function () {\n    const block = bot.blockAt(targetBlock)\n    const tool = bot.pathfinder.bestHarvestTool(block)\n    assert.deepStrictEqual(tool, itemsToGive[0])\n  })\n\n  it('getPathTo', function () {\n    const path = bot.pathfinder.getPathTo(bot.pathfinder.movements, new goals.GoalGetToBlock(targetBlock.x, targetBlock.y, targetBlock.z))\n    // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n    assert.strictEqual(path.status, 'success')\n    assert.ok(path.visitedNodes < 5, `Generated path visited nodes to high (${path.visitedNodes} < 5)`)\n    assert.ok(path.generatedNodes < 30, `Generated path nodes to high (${path.generatedNodes} < 30)`)\n    assert.ok(path.path.length === 3, `Generated path length wrong (${path.path.length} === 3)`)\n    assert.ok(path.time < 50, `Generated path took too long (${path.time} < 50)`)\n  })\n})\n\ndescribe('pathfinder Movement', function () {\n  const mcData = require('minecraft-data')(Version)\n  const Item = require('prismarine-item')(Version)\n  const Block = require('prismarine-block')(Version)\n\n  const targetBlock = new Vec3(12, 1, 8) // a gold block away from the spawn position\n  const spawnPos = new Vec3(8.5, 1, 8.5) // Center of the chunk & center of the block\n\n  /** @type { import('mineflayer').Bot & { pathfinder: import('mineflayer-pathfinder').Pathfinder }} */\n  let bot\n  /** @type { import('minecraft-protocol').Server } */\n  let server\n  /** @type { import('mineflayer-pathfinder').Movements } */\n  let defaultMovement\n\n  const itemsToGive = [new Item(mcData.itemsByName.diamond_pickaxe.id, 1), new Item(mcData.itemsByName.dirt.id, 64)]\n\n  before(async () => {\n    const chunk = flatMap(Version)\n    server = await newServer(server, chunk, spawnPos, Version, true)\n    bot = mineflayer.createBot({\n      username: 'player',\n      version: Version,\n      port: ServerPort\n    })\n    await once(bot, 'chunkColumnLoad')\n    itemsToGive.forEach(item => {\n      const slot = bot.inventory.firstEmptyHotbarSlot()\n      bot.inventory.slots[slot] = item\n    })\n    defaultMovement = new Movements(bot, mcData)\n    bot.loadPlugin(pathfinder)\n    bot.pathfinder.setMovements(defaultMovement)\n  })\n  after(() => server.close())\n\n  it('countScaffoldingItems', function () {\n    assert.strictEqual(defaultMovement.countScaffoldingItems(), 64)\n  })\n\n  it('getScaffoldingItem', function () {\n    assert.strictEqual(defaultMovement.getScaffoldingItem(), itemsToGive[1])\n  })\n\n  it('getBlock', function () {\n    assert.ok(defaultMovement.getBlock(targetBlock, 0, 0, 0).type === mcData.blocksByName.gold_block.id)\n  })\n\n  describe('safeToBreak world editing', function () {\n    this.afterAll(async () => {\n      defaultMovement.canDig = true\n      await bot.world.setBlock(targetBlock.offset(1, 0, 0), new Block(mcData.blocksByName.air.id, 0))\n    })\n\n    it('safeToBreak', async function () {\n      const block = bot.blockAt(targetBlock)\n      assert.ok(defaultMovement.safeToBreak(block))\n      defaultMovement.canDig = false\n      assert.ok(!defaultMovement.safeToBreak(block))\n      defaultMovement.canDig = true\n      await bot.world.setBlock(targetBlock.offset(1, 0, 0), new Block(mcData.blocksByName.water.id, 0, 0))\n      assert.ok(!defaultMovement.safeToBreak(block))\n    })\n  })\n\n  it('safeOrBreak', function () {\n    const block = defaultMovement.getBlock(targetBlock, 0, 0, 0)\n    const toBreak = []\n    const extraValue = defaultMovement.safeOrBreak(block, toBreak)\n    assert.ok(extraValue < 100, `safeOrBreak to high for block (${extraValue} < 100)`)\n    assert.ok(toBreak.length === 1, `safeOrBreak toBreak array wrong length ${toBreak.length} (${toBreak.length} === 1)`)\n  })\n\n  it('getMoveJumpUp', function () {\n    const block = defaultMovement.getBlock(targetBlock, -1, 0, 0)\n    const dir = new Vec3(1, 0, 0)\n    const neighbors = []\n    defaultMovement.getMoveJumpUp(block.position, dir, neighbors)\n    assert.ok(neighbors.length === 1, `getMoveJumpUp neighbors not right length (${neighbors.length} === 1)`)\n  })\n\n  it('getMoveForward', function () {\n    const dir = new Vec3(1, 0, 0)\n    const neighbors = []\n    defaultMovement.getMoveForward(targetBlock, dir, neighbors)\n    assert.ok(neighbors.length === 1, `getMoveForward neighbors not right length (${neighbors.length} === 1)`)\n  })\n\n  it('getMoveDiagonal', function () {\n    const dir = new Vec3(1, 0, 0)\n    const neighbors = []\n    defaultMovement.getMoveDiagonal(targetBlock, dir, neighbors)\n    assert.ok(neighbors.length === 1, `getMoveDiagonal neighbors not right length (${neighbors.length} === 1)`)\n  })\n\n  it('getLandingBlock', function () {\n    const node = targetBlock.offset(-1, 3, 0)\n    const dir = new Vec3(1, 0, 0)\n    const block = defaultMovement.getLandingBlock(node, dir)\n    assert.ok(block != null, 'Landing block is null')\n    if (!block) return\n    assert.ok(block.type === mcData.blocksByName.air.id, `getLandingBlock not the right block (${block.name} === air)`)\n    assert.ok(block.position.offset(0, -1, 0).distanceSquared(targetBlock) === 0, `getLandingBlock not landing (${block.position.offset(0, -1, 0).distanceSquared(targetBlock)}) on target block: ${defaultMovement.getBlock(block.position, 0, -1, 0).name}`)\n  })\n\n  it('getMoveDropDown', function () {\n    const dir = new Vec3(1, 0, 0)\n    const neighbors = []\n    defaultMovement.getMoveDropDown(targetBlock.offset(-1, 4, 0), dir, neighbors)\n    assert.ok(neighbors.length === 1, `getMoveDropDown neighbors not right length (${neighbors.length} === 1)`)\n  })\n\n  it('getMoveDown', function () {\n    const neighbors = []\n    defaultMovement.getMoveDown(targetBlock.offset(0, 4, 0), neighbors)\n    assert.ok(neighbors.length === 1, `getMoveDown neighbors not right length (${neighbors.length} === 1)`)\n  })\n\n  it('getMoveUp', function () {\n    const neighbors = []\n    defaultMovement.getMoveUp(targetBlock.offset(0, 1, 0), neighbors)\n    assert.ok(neighbors.length === 1, `getMoveUp neighbors not right length (${neighbors.length} === 1)`)\n  })\n\n  it('getNeighbors', function () {\n    const neighbors = defaultMovement.getNeighbors(targetBlock.offset(0, 1, 0))\n    assert.ok(neighbors.length > 0, 'getNeighbors length 0')\n  })\n})\n\ndescribe('Parkour path test', function () {\n  const mcData = require('minecraft-data')(Version)\n\n  const spawnPos = new Vec3(8.5, 1, 8.5) // Center of the chunk & center of the block\n\n  /** @type { import('mineflayer').Bot & { pathfinder: import('mineflayer-pathfinder').Pathfinder }} */\n  let bot\n  /** @type { import('minecraft-protocol').Server } */\n  let server\n  /** @type { import('mineflayer-pathfinder').Movements } */\n  let defaultMovement\n\n  const parkourSpawn1 = new Vec3(0.5, 3, 12.5)\n  const parkourSpawn2 = new Vec3(5.5, 3, 12.5)\n\n  before(async () => {\n    this.timeout(5000)\n    const chunk = await parkourMap(Version)\n    server = await newServer(server, chunk, spawnPos, Version, true)\n    bot = mineflayer.createBot({\n      username: 'player',\n      version: Version,\n      port: ServerPort\n    })\n    await once(bot, 'chunkColumnLoad')\n    defaultMovement = new Movements(bot, mcData)\n    bot.loadPlugin(pathfinder)\n    bot.pathfinder.setMovements(defaultMovement)\n  })\n  after(() => server.close())\n\n  it('getMoveParkourForward-1', function () {\n    const dirs = [new Vec3(0, 0, 1), new Vec3(0, 0, -1)]\n    for (let i = 0; i < dirs.length; i++) {\n      const dir = dirs[i] // only 2 dirs as the schematic parkour1.schem only has 2 other blocks to path to.\n      const neighbors = []\n      defaultMovement.getMoveParkourForward(parkourSpawn1, dir, neighbors)\n      assert.ok(neighbors.length === 1, `getMoveParkourForward jump off gold block neighbors not right length (${neighbors.length} === 1)`)\n    }\n  })\n\n  it('getMoveParkourForward-2', function () {\n    const dirs = [new Vec3(1, 0, 0), new Vec3(0, 0, 1), new Vec3(-1, 0, 0), new Vec3(0, 0, -1)]\n    for (let i = 0; i < dirs.length; i++) {\n      const dir = dirs[i]\n      const neighbors = []\n      defaultMovement.getMoveParkourForward(parkourSpawn2, dir, neighbors)\n      assert.ok(neighbors.length === 1, `getMoveParkourForward jump off gold block neighbors not right length (${neighbors.length} === 1)`)\n    }\n  })\n})\n\ndescribe('Physics test', function () {\n  const mcData = require('minecraft-data')(Version)\n\n  const spawnPos = new Vec3(8.5, 1, 8.5) // Center of the chunk & center of the block\n\n  /** @type { import('mineflayer').Bot & { pathfinder: import('mineflayer-pathfinder').Pathfinder }} */\n  let bot\n  /** @type { import('minecraft-protocol').Server } */\n  let server\n  /** @type { import('mineflayer-pathfinder').Movements } */\n  let defaultMovement\n\n  const parkourSpawn1 = new Vec3(0.5, 3, 12.5)\n  // const parkourSpawn2 = new Vec3(5.5, 3, 12.5)\n\n  before(async () => {\n    this.timeout(5000)\n    const chunk = await parkourMap(Version)\n    server = await newServer(server, chunk, spawnPos, Version, true)\n    bot = mineflayer.createBot({\n      username: 'player',\n      version: Version,\n      port: ServerPort\n    })\n    await once(bot, 'chunkColumnLoad')\n    defaultMovement = new Movements(bot, mcData)\n    bot.loadPlugin(pathfinder)\n    bot.pathfinder.setMovements(defaultMovement)\n  })\n  after(() => server.close())\n\n  it('simulateUntil', async function () {\n    this.slow(1000)\n    this.timeout(2000)\n    const ticksToSimulate = 10\n    const ticksPressForward = 5\n\n    bot.entity.position = parkourSpawn1.clone()\n    bot.entity.velocity = new Vec3(0, 0, 0)\n\n    // Wait for the bot to be on the ground so bot.entity.onGround == true\n    bot.clearControlStates()\n    await once(bot, 'physicTick')\n    await once(bot, 'physicTick')\n\n    const physics = new Physics(bot)\n\n    const simulatedSteps = []\n    const realSteps = []\n\n    const controller = (state, counter) => {\n      state.control.forward = counter <= ticksPressForward\n      state.control.jump = counter <= ticksPressForward\n      simulatedSteps.push(state.pos.toString() + ' Input:' + String(counter <= ticksPressForward))\n    }\n    const state = physics.simulateUntil(() => false, controller, ticksToSimulate)\n    simulatedSteps.push(state.pos.toString() + ' Input:false')\n\n    // We have to be carful to not mess up the event scheduling. for await on(bot, 'physicTick') seams to work.\n    // A for loop with just await once(bot, 'physicTick') does not always seam to work. What also works is attaching\n    // a listener to bot with bot.on('physicTick', listener) but this is a lot nicer.\n    let tick = 0\n    for await (const _ of on(bot, 'physicTick')) { // eslint-disable-line no-unused-vars\n      bot.setControlState('forward', tick <= ticksPressForward)\n      bot.setControlState('jump', tick <= ticksPressForward)\n      realSteps.push(bot.entity.position.toString() + ' Input:' + String(tick <= ticksPressForward))\n      tick++\n      if (tick > ticksToSimulate) break\n    }\n\n    bot.clearControlStates()\n    // console.info(bot.entity.position.toString(), console.info(state.pos.toString()))\n    assert.ok(bot.entity.position.distanceSquared(state.pos) < 0.01,\n      `Simulated states don't match Bot: ${bot.entity.position.toString()} !== Simulation: ${state.pos.toString()}`\n      // + '\\nSimulated Steps:\\n'\n      // + simulatedSteps.join('\\n') + '\\n'\n      // + 'Real steps:\\n'\n      // + realSteps.join('\\n')\n    )\n  })\n\n  // TODO: write test for simulateUntilNextTick\n})\n\ndescribe('pathfinder entity avoidance test', function () {\n  const mcData = require('minecraft-data')(Version)\n\n  const patherOptions = { resetEntityIntersects: false }\n  const maxPathTime = 50\n\n  const spawnPos = new Vec3(8.5, 1.0, 8.5) // Center of the chunk & center of the block\n\n  /** @type { import('mineflayer').Bot & { pathfinder: import('mineflayer-pathfinder').Pathfinder }} */\n  let bot\n  /** @type { import('minecraft-protocol').Server } */\n  let server\n  /** @type { import('prismarine-chunk').Chunk } */\n  let chunk\n\n  before(async () => {\n    chunk = await parkourMap(Version)\n    server = await newServer(server, chunk, spawnPos, Version, true)\n    bot = mineflayer.createBot({\n      username: 'player',\n      version: Version,\n      port: ServerPort\n    })\n    await once(bot, 'chunkColumnLoad')\n\n    bot.loadPlugin(pathfinder)\n    bot.pathfinder.setMovements(new Movements(bot, mcData))\n  })\n\n  after(() => {\n    bot.end()\n    bot = null\n    server.close()\n  })\n\n  /**\n  * Ensure algorithm does not impede performance when handling a large number of entities\n  */\n  it('entityIndexPerformance', () => {\n    const { performance } = require('perf_hooks')\n\n    const targetBlock = new Vec3(11.5, 2.0, 10.5) // a gold block away from the spawn position\n    const startPos = new Vec3(11.5, 2.0, 14.5) // Start point for test\n\n    const goal = new goals.GoalGetToBlock(targetBlock.x, targetBlock.y, targetBlock.z)\n\n    for (let i = 1; i <= 10000; i++) {\n      const pos = (i % 2) === 0 ? new Vec3(10.5, 2.0, 12.5) : new Vec3(12.5, 2.0, 12.5)\n      bot.entities[i] = { name: 'testEntity', position: pos, height: 2.0, width: 1.0 }\n    }\n\n    const beforeTime = performance.now()\n\n    const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal)\n    const { value: { result } } = generator.next()\n\n    const timeElapsed = performance.now() - beforeTime\n\n    bot.pathfinder.movements.clearCollisionIndex()\n    for (let i = 1; i <= 10000; i++) {\n      delete bot.entities[i]\n    }\n\n    assert.ok(timeElapsed < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n  })\n\n  /**\n   * Tests if bot will prefer a basic path with less entities\n   * The test course is a 3x3x2 with a divider in the center\n   * [O] = Open, [W] = Wall, [S] = Start, [E] = End\n   *    W E W\n   *  W O O O W\n   *  W O W O W\n   *  W O O O W\n   *    W S W\n   */\n  describe('Weighted Path Avoidance', () => {\n    const targetBlock = new Vec3(11.5, 2.0, 10.5) // a gold block away from the spawn position\n    const startPos = new Vec3(11.5, 2.0, 14.5) // Start point for test\n    const firstLeftNode = new Vec3(10.5, 2.0, 12.5)\n    const firstRightNode = new Vec3(12.5, 2.0, 12.5)\n\n    const goal = new goals.GoalGetToBlock(targetBlock.x, targetBlock.y, targetBlock.z)\n\n    beforeEach((done) => {\n      bot.pathfinder.movements.clearCollisionIndex()\n      setTimeout(done, 100)\n    })\n\n    /**\n     * By default, algorithm will favor the Left Path\n     * [X] = Ent, [O] = Open, [W] = Wall\n     *   O O O\n     *   O W O\n     *   O O O\n     */\n    it('defaultPath', () => {\n      const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal, patherOptions)\n      const { value: { result } } = generator.next()\n      const path = result.path\n\n      // Look at first and second nodes incase diagonal movements are used\n      const leftBranch = (path[0].equals(firstLeftNode) || path[1].equals(firstLeftNode))\n      const rightBranch = (path[0].equals(firstRightNode) || path[1].equals(firstRightNode))\n\n      // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n      assert.strictEqual(result.status, 'success')\n      assert.ok(result.time < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n      assert.ok(path.length === 3, `Generated path length wrong (${path.length} === 3)`)\n      assert.ok(leftBranch === true, `Generated path did not follow Left Branch [Left Branch: ${leftBranch}, Right Branch: ${rightBranch}]`)\n    })\n\n    /**\n     * Ensure path with weight is avoided\n     * [X] = Ent, [O] = Open, [W] = Wall\n     *   O O O\n     *   O W X\n     *   O O O\n     */\n    it('rightBranchObstructed', () => {\n      add1x2Weight(bot.pathfinder.movements.entityIntersections, 12, 2, 12)\n\n      const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal, patherOptions)\n      const { value: { result } } = generator.next()\n      const path = result.path\n\n      // Look at first and second nodes incase diagonal movements are used\n      const leftBranch = (path[0].equals(firstLeftNode) || path[1].equals(firstLeftNode))\n      const rightBranch = (path[0].equals(firstRightNode) || path[1].equals(firstRightNode))\n\n      // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n      assert.strictEqual(result.status, 'success')\n      assert.ok(result.time < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n      assert.ok(path.length === 3, `Generated path length wrong (${path.length} === 3)`)\n      assert.ok(leftBranch === true, `Generated path did not follow Left Branch [Left Branch: ${leftBranch}, Right Branch: ${rightBranch}]`)\n    })\n\n    /**\n     * Ensure path with more weight is avoided\n     * [X] = Ent, [O] = Open, [W] = Wall\n     *   O O O\n     *   X W X\n     *   X O O\n     */\n    it('leftBranchMoreObstructed', () => {\n      add1x2Weight(bot.pathfinder.movements.entityIntersections, 12, 2, 12)\n      add1x2Weight(bot.pathfinder.movements.entityIntersections, 10, 2, 12)\n      add1x2Weight(bot.pathfinder.movements.entityIntersections, 10, 2, 13)\n\n      const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal, patherOptions)\n      const { value: { result } } = generator.next()\n      const path = result.path\n\n      // Look at first and second nodes incase diagonal movements are used\n      const leftBranch = (path[0].equals(firstLeftNode) || path[1].equals(firstLeftNode))\n      const rightBranch = (path[0].equals(firstRightNode) || path[1].equals(firstRightNode))\n\n      // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n      assert.strictEqual(result.status, 'success')\n      assert.ok(result.time < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n      assert.ok(path.length === 3, `Generated path length wrong (${path.length} === 3)`)\n      assert.ok(rightBranch === true, `Generated path did not follow Right Branch [Left Branch: ${leftBranch}, Right Branch: ${rightBranch}]`)\n    })\n\n    /**\n     * Ensure blocks adjacent to diagonal nodes are detected\n     * [X] = Ent, [O] = Open, [W] = Wall\n     *   O O X\n     *   X W O\n     *   O O X\n     */\n    it('rightBranchDiagsClear', () => {\n      add1x2Weight(bot.pathfinder.movements.entityIntersections, 12, 2, 13)\n      add1x2Weight(bot.pathfinder.movements.entityIntersections, 12, 2, 11)\n      add1x2Weight(bot.pathfinder.movements.entityIntersections, 10, 2, 12)\n\n      const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal, patherOptions)\n      const { value: { result } } = generator.next()\n      const path = result.path\n\n      // Look at first and second nodes incase diagonal movements are used\n      const leftBranch = (path[0].equals(firstLeftNode) || path[1].equals(firstLeftNode))\n      const rightBranch = (path[0].equals(firstRightNode) || path[1].equals(firstRightNode))\n\n      // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n      assert.strictEqual(result.status, 'success')\n      assert.ok(result.time < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n      assert.ok(path.length === 3, `Generated path length wrong (${path.length} === 3)`)\n      assert.ok(leftBranch === true, `Generated path did not follow Left Branch [Left Branch: ${leftBranch}, Right Branch: ${rightBranch}]`)\n    })\n\n    /**\n     * Ensure blocks adjacent to diagonal nodes are detected\n     * [X] = Ent, [O] = Open, [W] = Wall\n     *   X O O\n     *   O W X\n     *   X O O\n     */\n    it('leftBranchDiagsClear', () => {\n      add1x2Weight(bot.pathfinder.movements.entityIntersections, 12, 2, 12)\n      add1x2Weight(bot.pathfinder.movements.entityIntersections, 10, 2, 13)\n      add1x2Weight(bot.pathfinder.movements.entityIntersections, 10, 2, 11)\n\n      const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal, patherOptions)\n      const { value: { result } } = generator.next()\n      const path = result.path\n\n      // Look at first and second nodes incase diagonal movements are used\n      const leftBranch = (path[0].equals(firstLeftNode) || path[1].equals(firstLeftNode))\n      const rightBranch = (path[0].equals(firstRightNode) || path[1].equals(firstRightNode))\n\n      // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n      assert.strictEqual(result.status, 'success')\n      assert.ok(result.time < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n      assert.ok(path.length === 3, `Generated path length wrong (${path.length} === 3)`)\n      assert.ok(rightBranch === true, `Generated path did not follow Right Branch [Left Branch: ${leftBranch}, Right Branch: ${rightBranch}]`)\n    })\n  })\n\n  /**\n   * Tests if bot will try to path where they cannot build due to an entity and whether it will\n   * try to break a block that would potentially cause an entity to fall.\n   * The test course is a 2x2x4 pit where the start is at the bottom and the end is at the top\n   * [O] = Open, [W] = Wall, [S] = Start, [E] = End\n   *   W W W E\n   *   W O O W\n   *   W S O W\n   *   W W W W\n   */\n  describe('Construction Path Avoidance', () => {\n    const Item = require('prismarine-item')(Version)\n\n    const scaffoldItemId = mcData.itemsByName.dirt.id\n    const groundYPos = 2\n    const lidYPos = 5\n    const forwardPos = { x: 11, z: 6 }\n    const leftPos = { x: 10, z: 6 }\n    const rightPos = { x: 11, z: 7 }\n    const backPos = { x: 10, z: 7 }\n    const targetBlock = new Vec3(forwardPos.x + 1.5, lidYPos + 1.0, forwardPos.z - 0.5) // a gold block away from the spawn position. One block diagonal from forward\n    const startPos = new Vec3(backPos.x + 0.5, groundYPos, backPos.z + 0.5) // Start point for test\n    const firstLeftNode = new Vec3(leftPos.x + 0.5, groundYPos, leftPos.z + 0.5)\n    const firstRightNode = new Vec3(rightPos.x + 0.5, groundYPos, rightPos.z + 0.5)\n    const firstForwardNode = new Vec3(forwardPos.x + 0.5, groundYPos, forwardPos.z + 0.5)\n    const firstBackNode = startPos.clone().plus(new Vec3(-0.5, 1, -0.5)) // Jump up isn't going to half block and targets one block higher\n\n    const blockersToPlace = [forwardPos, leftPos, rightPos, backPos]\n    const goal = new goals.GoalGetToBlock(targetBlock.x, targetBlock.y, targetBlock.z)\n\n    /** @type { import('minecraft-protocol').Client } */\n    let serverClient\n    /** @type { number } */\n    let hotbarSlot\n\n    before(() => {\n      serverClient = Object.values(server.clients)[0]\n      hotbarSlot = bot.inventory.firstEmptyHotbarSlot()\n    })\n\n    beforeEach((done) => {\n      bot.pathfinder.movements.clearCollisionIndex()\n      bot.inventory.slots[hotbarSlot] = new Item(scaffoldItemId, 64)\n      setTimeout(done, 100)\n    })\n\n    afterEach(async () => {\n      blockersToPlace.forEach(hPos => {\n        const blockPos = { x: hPos.x, y: lidYPos, z: hPos.z }\n        serverClient.write('block_change', { location: blockPos, type: mcData.blocksByName.air.id })\n        chunk.setBlockType(new Vec3(blockPos.x, blockPos.y, blockPos.z), mcData.blocksByName.air.id)\n      })\n      serverClient.write('map_chunk', generateChunkPacket(chunk))\n      await once(bot, 'chunkColumnLoad')\n    })\n\n    /**\n     * By default, algorithm will favor the Backward Path\n     * [X] = Ent Below, [+] = Ent Above a Block, [O] = Open, [W] = Wall\n     *   W W W W\n     *   W O O W\n     *   W O O W\n     *   W W W W\n     */\n    it('defaultPath', () => {\n      const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal, patherOptions)\n      const { value: { result } } = generator.next()\n      const path = result.path\n\n      // Look at first and second nodes incase diagonal movements are used\n      const leftBranch = (path[0].equals(firstLeftNode) || path[1].equals(firstLeftNode))\n      const rightBranch = (path[0].equals(firstRightNode) || path[1].equals(firstRightNode))\n      const forwardBranch = (path[0].equals(firstForwardNode) || path[1].equals(firstForwardNode))\n      const backwardBranch = (path[0].equals(firstBackNode) || path[1].equals(firstBackNode))\n\n      // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n      assert.strictEqual(result.status, 'success')\n      assert.ok(result.time < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n      assert.ok(path.length === 6, `Generated path length wrong (${path.length} === 6)`)\n      assert.ok(backwardBranch === true, `Generated path did not follow Backward Branch [Left Branch: ${leftBranch}, Right Branch: ${rightBranch}, Forward Branch: ${forwardBranch}, Backward Branch: ${backwardBranch}]`)\n    })\n\n    /**\n     * Ensure bot finds a path when it cannot break left blocker with ent on top\n     * [X] = Ent Below, [+] = Ent Above a Block, [O] = Open, [W] = Wall\n     *   W W W W\n     *   W O O W\n     *   W + O W\n     *   W W W W\n     */\n    it('backPathObstructed', async () => {\n      const blockPos = { x: backPos.x, y: lidYPos, z: backPos.z }\n      serverClient.write('block_change', { location: blockPos, type: mcData.blocksByName.dirt.id })\n      chunk.setBlockType(new Vec3(blockPos.x, blockPos.y, blockPos.z), mcData.blocksByName.dirt.id)\n      add1x2Weight(bot.pathfinder.movements.entityIntersections, blockPos.x, blockPos.y + 1, blockPos.z)\n\n      serverClient.write('map_chunk', generateChunkPacket(chunk))\n      await once(bot, 'chunkColumnLoad')\n\n      const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal, patherOptions)\n      const { value: { result } } = generator.next()\n      const path = result.path\n\n      // Look at first and second nodes incase diagonal movements are used\n      const leftBranch = (path[0].equals(firstLeftNode) || path[1].equals(firstLeftNode))\n      const rightBranch = (path[0].equals(firstRightNode) || path[1].equals(firstRightNode))\n      const forwardBranch = (path[0].equals(firstForwardNode) || path[1].equals(firstForwardNode))\n      const backwardBranch = (path[0].equals(firstBackNode) || path[1].equals(firstBackNode))\n\n      // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n      assert.strictEqual(result.status, 'success')\n      assert.ok(result.time < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n      assert.ok(path.length === 6, `Generated path length wrong (${path.length} === 6)`)\n      assert.ok(rightBranch === true, `Generated path did not follow Right Branch [Left Branch: ${leftBranch}, Right Branch: ${rightBranch}, Forward Branch: ${forwardBranch}, Backward Branch: ${backwardBranch}]`)\n    })\n\n    /**\n     * If there are blocks capping the pit with an entity above each block, ensure bot cannot path since\n     * there are no blocks that can be broken without potentially dropping an entity. Bot is expected\n     * to follow forward branch for as far as possible\n     * [X] = Ent Below, [+] = Ent Above a Block, [O] = Open, [W] = Wall\n     *   W W W W\n     *   W + + W\n     *   W + + W\n     *   W W W W\n     */\n    it('noPathsBreakingObstructed', async () => {\n      blockersToPlace.forEach(hPos => {\n        const blockPos = { x: hPos.x, y: lidYPos, z: hPos.z }\n        serverClient.write('block_change', { location: blockPos, type: mcData.blocksByName.dirt.id })\n        chunk.setBlockType(new Vec3(blockPos.x, blockPos.y, blockPos.z), mcData.blocksByName.dirt.id)\n        add1x2Weight(bot.pathfinder.movements.entityIntersections, blockPos.x, blockPos.y + 1, blockPos.z)\n      })\n      serverClient.write('map_chunk', generateChunkPacket(chunk))\n      await once(bot, 'chunkColumnLoad')\n\n      const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal, patherOptions)\n      const { value: { result } } = generator.next()\n      const path = result.path\n\n      // Look at first and second nodes incase diagonal movements are used\n      const leftBranch = (path[0].equals(firstLeftNode) || path[1].equals(firstLeftNode))\n      const rightBranch = (path[0].equals(firstRightNode) || path[1].equals(firstRightNode))\n      const forwardBranch = (path[0].equals(firstForwardNode) || path[1].equals(firstForwardNode))\n      const backwardBranch = (path[0].equals(firstBackNode) || path[1].equals(firstBackNode))\n\n      // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n      assert.strictEqual(result.status, 'noPath')\n      assert.ok(result.time < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n      assert.ok(path.length === 2, `Generated path length wrong (${path.length} === 2)`)\n      assert.ok(forwardBranch === true, `Generated path did not attempt to follow Forward Branch [Left Branch: ${leftBranch}, Right Branch: ${rightBranch}, Forward Branch: ${forwardBranch}, Backward Branch: ${backwardBranch}]`)\n    })\n\n    /**\n     * If there are blocks capping the pit with an entity above each block, ensure bot can path\n     * if allowed in the movements configuration\n     * [X] = Ent Below, [+] = Ent Above a Block, [O] = Open, [W] = Wall\n     *   W W W W\n     *   W + + W\n     *   W + + W\n     *   W W W W\n     */\n    it('canPathWithBreakingObstructed', async () => {\n      blockersToPlace.forEach(hPos => {\n        const blockPos = { x: hPos.x, y: lidYPos, z: hPos.z }\n        serverClient.write('block_change', { location: blockPos, type: mcData.blocksByName.dirt.id })\n        chunk.setBlockType(new Vec3(blockPos.x, blockPos.y, blockPos.z), mcData.blocksByName.dirt.id)\n        add1x2Weight(bot.pathfinder.movements.entityIntersections, blockPos.x, blockPos.y + 1, blockPos.z)\n      })\n      serverClient.write('map_chunk', generateChunkPacket(chunk))\n      await once(bot, 'chunkColumnLoad')\n\n      bot.pathfinder.movements.dontMineUnderFallingBlock = false\n      const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal, patherOptions)\n      const { value: { result } } = generator.next()\n      const path = result.path\n      bot.pathfinder.movements.dontMineUnderFallingBlock = true\n\n      // Look at first and second nodes incase diagonal movements are used\n      const leftBranch = (path[0].equals(firstLeftNode) || path[1].equals(firstLeftNode))\n      const rightBranch = (path[0].equals(firstRightNode) || path[1].equals(firstRightNode))\n      const forwardBranch = (path[0].equals(firstForwardNode) || path[1].equals(firstForwardNode))\n      const backwardBranch = (path[0].equals(firstBackNode) || path[1].equals(firstBackNode))\n\n      // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n      assert.strictEqual(result.status, 'success')\n      assert.ok(result.time < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n      assert.ok(path.length === 6, `Generated path length wrong (${path.length} === 6)`)\n      assert.ok(forwardBranch === true, `Generated path did not follow Forward Branch [Left Branch: ${leftBranch}, Right Branch: ${rightBranch}, Forward Branch: ${forwardBranch}, Backward Branch: ${backwardBranch}]`)\n    })\n\n    /**\n     * If there are entities filling the entire build area, ensure bot cannot path since\n     * entities will prevent any block placement. Bot is expected to follow forward branch\n     * for as far as possible\n     * [X] = Ent Below, [+] = Ent Above a Block, [O] = Open, [W] = Wall\n     *   W W W W\n     *   W X X W\n     *   W X X W\n     *   W W W W\n     */\n    it('noPathsBuildingObstructed', () => {\n      blockersToPlace.forEach(hPos => {\n        add1x2Weight(bot.pathfinder.movements.entityIntersections, hPos.x, groundYPos, hPos.z)\n      })\n\n      const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal, patherOptions)\n      const { value: { result } } = generator.next()\n      const path = result.path\n\n      const leftBranch = path[0].equals(firstLeftNode)\n      const rightBranch = path[0].equals(firstRightNode)\n      const forwardBranch = path[0].equals(firstForwardNode)\n      const backwardBranch = path[0].equals(firstBackNode)\n\n      // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n      assert.strictEqual(result.status, 'noPath')\n      assert.ok(result.time < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n      assert.ok(path.length === 1, `Generated path length wrong (${path.length} === 1)`)\n      assert.ok(forwardBranch === true, `Generated path did not attempt to follow Forward Branch [Left Branch: ${leftBranch}, Right Branch: ${rightBranch}, Forward Branch: ${forwardBranch}, Backward Branch: ${backwardBranch}]`)\n    })\n\n    /**\n     * If there are entities filling the entire build area except for one space, ensure bot finds a path\n     * [X] = Ent Below, [+] = Ent Above a Block, [O] = Open, [W] = Wall\n     *   W W W W\n     *   W O X W\n     *   W X X W\n     *   W W W W\n     */\n    it('singlePathUnobstructed', () => {\n      blockersToPlace.forEach(hPos => {\n        if ((hPos.x !== leftPos.x) || (hPos.z !== leftPos.z)) {\n          add1x2Weight(bot.pathfinder.movements.entityIntersections, hPos.x, groundYPos, hPos.z)\n        }\n      })\n\n      const generator = bot.pathfinder.getPathFromTo(bot.pathfinder.movements, startPos, goal, patherOptions)\n      const { value: { result } } = generator.next()\n      const path = result.path\n\n      // Look at first and second nodes incase diagonal movements are used\n      const leftBranch = (path[0].equals(firstLeftNode) || path[1].equals(firstLeftNode))\n      const rightBranch = (path[0].equals(firstRightNode) || path[1].equals(firstRightNode))\n      const forwardBranch = (path[0].equals(firstForwardNode) || path[1].equals(firstForwardNode))\n      const backwardBranch = (path[0].equals(firstBackNode) || path[1].equals(firstBackNode))\n\n      // All depends on the actually path that gets generated. If target block is moved some were else these values have to change.\n      assert.strictEqual(result.status, 'success')\n      assert.ok(result.time < maxPathTime, `Generated path took too long (${result.time} < ${maxPathTime})`)\n      assert.ok(path.length === 6, `Generated path length wrong (${path.length} === 6)`)\n      assert.ok(leftBranch === true, `Generated path did not follow Left Branch [Left Branch: ${leftBranch}, Right Branch: ${rightBranch}, Forward Branch: ${forwardBranch}, Backward Branch: ${backwardBranch}]`)\n    })\n  })\n})\n"
  }
]