LookAtMySuitBot/js/node_modules/mineflayer/docs/mineflayer.ipynb

534 lines
38 KiB
Plaintext
Raw Normal View History

2023-12-24 20:08:39 -05:00
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "mineflayer.ipynb",
"provenance": [],
"collapsed_sections": [],
"authorship_tag": "ABX9TyO3/6T3HTMoRxL7FoQ4bWrl",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/github/PrismarineJS/mineflayer/blob/master/docs/mineflayer.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "2BAYqsdOgKNJ"
},
"source": [
"# Using mineflayer in Python\n",
"\n",
"This is a tutorial on how to use mineflayer in Python. This example will connect you to the PrismarineJS test server. You can join it with prismarine-viewer or your Minecraft client at server IP **pjs.deptofcraft.com:25565**.\n",
"\n",
"If you're new to Jupyter Notebooks, you can press the \"Play\" button at the left of each code block to run it. Make sure that you run the blocks in a correct order."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "qM2rVyxGf2Yv"
},
"source": [
"## Setup"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "K2ol06QOhL6s"
},
"source": [
"First, make sure you have Python version 3.10 and Node.js version 18 or newer installed. You can get Node.js 18 it from https://nodejs.org/en/download or use [Node.js version managers](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm#using-a-node-version-manager-to-install-nodejs-and-npm) like [`nvm`](https://github.com/creationix/nvm) or [`n`](https://github.com/tj/n) to install via the command line. Here we'll use `n` to install Node.js v18, then check our Node and Python versions:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "8zCSpx8Bif5m",
"outputId": "90ebac14-fc75-4136-f81d-34c5b2033da0"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"v18.17.1\n",
"Python 3.10.12\n"
]
}
],
"source": [
"# Use `n` to install nodejs 18, if it's not already installed:\n",
"!curl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n | bash -s lts > /dev/null\n",
"# Now write the Node.js and Python version to the console\n",
"!node --version\n",
"!python --version"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "C7omnDs3lNaV"
},
"source": [
"Now, we can use pip to install the `javascript` Python package to access Node.js libraries from Python."
]
},
{
"cell_type": "code",
"metadata": {
"id": "DKnwzSZQ8Taf"
},
"source": [
" !pip install javascript"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "_RAKlcScgKtV"
},
"source": [
"## Usage"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "bxAdFbBfmdCd"
},
"source": [
"If all is well, we can import the `javascript` library. We can then import the `require` function which works similarly to the `require` function in Node.js, but does the dependency management for us.\n",
"\n",
"You may notice the extra imports : On, Once, off and AsyncTask. These will be discussed later on.\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "54Lnq3aH4Tee"
},
"source": [
"from javascript import require, On, Once, AsyncTask, once, off"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "cy7-0cWxdhU8"
},
"source": [
"We can now import Mineflayer"
]
},
{
"cell_type": "code",
"metadata": {
"id": "8jgkTVniDPUZ"
},
"source": [
"mineflayer = require('mineflayer')"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "WBAj5rSkgjKX"
},
"source": [
"Once we've done that, we can create a new `bot` instance, through the `createBot` function. You can see the docs for this function [here](https://github.com/PrismarineJS/mineflayer/blob/master/docs/api.md#bot). In the line below we specify a hostname and a port for the server, but do not pass any `auth` or `password` options, so it will connect to the server in offline mode.\n",
"\n",
"Below that, we also a call to the `once` function, which pauses the thread until an event has been triggered, then returns the output. Here, we print out \"I spawned\" after the `login` event has been triggered on `bot`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "1gfZSAUCDVMg"
},
"outputs": [],
"source": [
"random_number = id([]) % 1000 # Give us a random number upto 1000\n",
"BOT_USERNAME = f'colab_{random_number}'\n",
"\n",
"bot = mineflayer.createBot({ 'host': 'pjs.deptofcraft.com', 'port': 25565, 'username': BOT_USERNAME, 'hideErrors': False })\n",
"\n",
"# The spawn event\n",
"once(bot, 'login')\n",
"bot.chat('I spawned')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "yvYZYbi0k8Za"
},
"source": [
"If your bot spawned, we can now take a look at the bot's position"
]
},
{
"cell_type": "code",
"metadata": {
"id": "swMd1VvXYuKn"
},
"source": [
"bot.entity.position"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "EdSjlgmilZ3O"
},
"source": [
"### Listening to events"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "23FTp0XrioMg"
},
"source": [
"You can register an event handler with the `@On` or `@Once` decorator. This decorator takes two arguments, first it's the **Event Emitter** (the object that is sending events) and the second is the **event name**, what event you want to listen to. *Do not use the .on or .once methods on bot, use the decorators instead.*\n",
"\n",
"A decorator always has a function under it which is being decorated, which can have any name. The first parameter to any event emitter callback is the `this` argument. \n",
"\n",
"In the code below, we create an event emitter on `bot` that listens to `playerJoin` events, then print that out."
]
},
{
"cell_type": "code",
"metadata": {
"id": "s8QGmC4nHjnH"
},
"source": [
"@On(bot, 'playerJoin')\n",
"def end(this, player):\n",
" bot.chat('Someone joined!')"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "Onkn-TDsne9P"
},
"source": [
"In Python, you cannot leave any arguments for an event handler callback blank like in JavaScript. Instead, you can use the asterisk (`*`) operator in Python to capture all remaining arguments to the right, much like the `...` rest/spread operator in JavaScript. The parameter with the asterisk will be a tuple containing the captured arguments.\n",
"\n",
"You can stop listening for events through an event handler by using the imported `off` function. It takes three parameters: the emitter, event name, and a reference to the Python function.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "S4y9qAe6oh8H"
},
"source": [
"@On(bot, 'chat')\n",
"def onChat(this, user, message, *rest):\n",
" print(f'{user} said \"{message}\"')\n",
"\n",
" # If the message contains stop, remove the event listener and stop logging.\n",
" if 'stop' in message:\n",
" off(bot, 'chat', onChat)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "OybQNxGAq4P2"
},
"source": [
"You need to `off` all the event listeners you listen to with `@On`, else the Python process won't exit until all of the active event emitters have been off'ed. If you only need to listen once, you can use the `@Once` decroator like in the example above."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "iOzZeWfHozeX"
},
"source": [
"## Asynchronous tasks\n",
"\n",
"By default, all the operations you do run on the main thread. This means you can only do one thing at a time. To multitask, you can use the `@AsyncTask` decroator to run a function in a new thread, while not obstructing the main thread."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "xJUk8b21pOzg"
},
"source": [
"### Block breaking\n",
"\n",
"Take a look at the example below. Here we listen for a \"break\" trigger in a chat message, then we start digging the block underneath, while simultaneously sending a message that the bot has \"started digging\"."
]
},
{
"cell_type": "code",
"metadata": {
"id": "yhoAlhAhpSTL"
},
"source": [
"@On(bot, 'chat')\n",
"def breakListener(this, sender, message, *args):\n",
" if sender and (sender != BOT_USERNAME):\n",
" if 'break' in message:\n",
" pos = bot.entity.position.offset(0, -1, 0)\n",
" blockUnder = bot.blockAt(pos)\n",
" if bot.canDigBlock(blockUnder):\n",
" bot.chat(f\"I'm breaking the '{blockUnder.name}' block underneath\")\n",
" # The start=True parameter means to immediately invoke the function underneath\n",
" # If left blank, you can start it with the `start()` function later on.\n",
" try:\n",
" @AsyncTask(start=True)\n",
" def break_block(task):\n",
" bot.dig(blockUnder)\n",
" bot.chat('I started digging!')\n",
" except Exception as e:\n",
" bot.chat(f\"I had an error {e}\")\n",
" else:\n",
" bot.chat(f\"I can't break the '{blockUnder.name}' block underneath\")\n",
" if 'stop' in message:\n",
" off(bot, 'chat', breakListener)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "JMgoMA-MriAt"
},
"source": [
"## Using mineflayer plugins\n",
"\n",
"Pick the plugin you want from the list [here](https://github.com/PrismarineJS/mineflayer#third-party-plugins), then `require()` it and register it to the bot. Some plugins have different ways to register to the bot, look at the plugin's README for usage steps."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "OVAJCyxcsfig"
},
"source": [
"### mineflayer-pathfinder\n",
"\n",
"`mineflayer-pathfinder` is a essential plugin that helps your bot move between places through A* pathfinding. Let's import it:"
]
},
{
"cell_type": "code",
"metadata": {
"id": "mH6eXm8TtTKh"
},
"source": [
"pathfinder = require('mineflayer-pathfinder')\n",
"bot.loadPlugin(pathfinder.pathfinder)\n",
"# Create a new minecraft-data instance with the bot's version\n",
"mcData = require('minecraft-data')(bot.version)\n",
"# Create a new movements class\n",
"movements = pathfinder.Movements(bot, mcData)\n",
"# How far to be fromt the goal\n",
"RANGE_GOAL = 1"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "Ju8MPkSauTBb"
},
"source": [
"Now let's have create a goal for the bot to move to where another player wants, based on a chat message."
]
},
{
"cell_type": "code",
"metadata": {
"id": "8jIp8bxnudDK"
},
"source": [
"bot.removeAllListeners('chat')\n",
"@On(bot, 'chat')\n",
"def handleMsg(this, sender, message, *args):\n",
" if sender and (sender != BOT_USERNAME):\n",
" bot.chat('Hi, you said ' + message)\n",
" if 'come' in message:\n",
" player = bot.players[sender]\n",
" target = player.entity\n",
" if not target:\n",
" bot.chat(\"I don't see you !\")\n",
" return\n",
" pos = target.position\n",
" bot.pathfinder.setMovements(movements)\n",
" bot.pathfinder.setGoal(pathfinder.goals.GoalNear(pos.x, pos.y, pos.z, RANGE_GOAL))\n",
" if 'stop' in message:\n",
" off(bot, 'chat', handleMsg)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "K36XDP09k1aH"
},
"source": [
"## Analyzing the world"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "xK1Ww1ACmLZl"
},
"source": [
"You can also interact with mineflayer through any other Python package."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "5QatUqxeW6b_"
},
"source": [
"Let's analyze some block frequencies..."
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 417
},
"id": "k2XyRgzi8otw",
"outputId": "a4de38b7-ec53-4e38-df0c-8b7f1067965e"
},
"source": [
"import matplotlib.pyplot as plt\n",
"figure = plt.figure()\n",
"axes = figure.add_axes([0,0,1,1])\n",
"Vec3 = require('vec3').Vec3\n",
"\n",
"columns = bot.world.getColumns()\n",
"block_freqs = {}\n",
"for c in range(0, 3): # iterate through some of the loaded chunk columns\n",
" cc = columns[c].column\n",
" for y in range(1, 40):\n",
" for x in range(1, 16):\n",
" for z in range(1, 16):\n",
" block = cc.getBlock(Vec3(x, y, z))\n",
" if block.name in block_freqs:\n",
" block_freqs[block.name] += 1\n",
" else:\n",
" block_freqs[block.name] = 1\n",
"\n",
"print(block_freqs)\n",
"axes.bar(block_freqs.keys(), block_freqs.values())\n",
"plt.xticks(rotation=45)\n",
"plt.show()"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"text": [
"{'bedrock': 1321, 'stone': 19258, 'diorite': 1123, 'lava': 64, 'granite': 1704, 'andesite': 1459, 'redstone_ore': 68, 'iron_ore': 156, 'coal_ore': 282, 'gold_ore': 26, 'lapis_ore': 5, 'dirt': 570, 'emerald_ore': 3, 'diamond_ore': 9, 'gravel': 66, 'air': 211}\n"
],
"name": "stdout"
},
{
"output_type": "display_data",
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAeUAAAFrCAYAAADvipHKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3deZwcZbX/8c8hYSchAUIMSdgDCogBIvuOhLAGENkhAhJAULmigiiLCAgKqAjiDRoNXnYREiUIARFUQEgAWUQkLF6SGyAIij9U1vP745xiKsNMpqe7Z6Ym832/Xv2a7qerq57qrqnzbPWUuTsiIiLS8xbr6QyIiIhIUFAWERGpCAVlERGRilBQFhERqQgFZRERkYpQUBYREamI/h0tYGYjgSuAoYADk9z9u2a2AnAtsDrwHLC/u79qZgZ8F9gN+BfwSXd/MNc1Afhqrvpsd5+S6ZsAPwGWBqYDn/MOrtVaaaWVfPXVV+/MvoqIiPS4WbNmvezuQ9p6zzq6TtnMhgHD3P1BMxsAzAL2Bj4JvOLu55nZKcBgdz/ZzHYDPkME5c2A77r7ZhnEZwJjiOA+C9gkA/n9wGeBPxBB+WJ3v2Vh+RozZozPnDmzxq9ARESkGsxslruPaeu9Dpuv3X1eUdN1938CTwDDgfHAlFxsChGoyfQrPNwHDMrAvgsww91fcfdXgRnAuHxvoLvfl7XjK0rrEhER6TM61adsZqsDGxE12qHuPi/feoFo3oYI2M+XPjYn0xaWPqeN9La2P9HMZprZzPnz53cm6yIiIpVXc1A2s+WAG4AT3f218ntZw+3y+TrdfZK7j3H3MUOGtNkcLyIi0mvVFJTNbHEiIF/p7j/P5Bez6bnod34p0+cCI0sfH5FpC0sf0Ua6iIhIn9JhUM7R1D8CnnD3i0pvTQMm5PMJwNRS+uEWNgf+kc3ctwJjzWywmQ0GxgK35nuvmdnmua3DS+sSERHpMzq8JArYCjgMeNTMHs60U4HzgOvM7Cjgr8D++d50YuT1bOKSqCMA3P0VM/s68EAud5a7v5LPP03LJVG35ENERKRP6fCSqKrSJVEiItIbNXRJlIiIiHQPBWUREZGKUFAWERGpCAVlERGRilBQFhERqYhaLomSJlj9lJubur7nztu9qesTEZGep5qyiIhIRSgoi4iIVISCsoiISEUoKIuIiFSEgrKIiEhFKCiLiIhUhIKyiIhIRSgoi4iIVISCsoiISEUoKIuIiFSEgrKIiEhFKCiLiIhUhIKyiIhIRSgoi4iIVISCsoiISEUoKIuIiFSEgrKIiEhFKCiLiIhUhIKyiIhIRSgoi4iIVISCsoiISEV0GJTNbLKZvWRmj5XSrjWzh/PxnJk9nOmrm9m/S+/9oPSZTczsUTObbWYXm5ll+gpmNsPMnsq/g7tiR0VERKqulpryT4Bx5QR3P8DdR7v7aOAG4Oelt58u3nP3Y0vplwFHA6PyUazzFOAOdx8F3JGvRURE+pwOg7K73w280tZ7WdvdH7h6Yesws2HAQHe/z90duALYO98eD0zJ51NK6SIiIn1Ko33K2wAvuvtTpbQ1zOwhM7vLzLbJtOHAnNIyczINYKi7z8vnLwBD29uYmU00s5lmNnP+/PkNZl1ERKRaGg3KB7FgLXkesKq7bwR8HrjKzAbWurKsRftC3p/k7mPcfcyQIUPqzbOIiEgl9a/3g2bWH9gX2KRIc/c3gDfy+SwzexpYB5gLjCh9fESmAbxoZsPcfV42c79Ub55ERER6s0Zqyh8D/uzu7zVLm9kQM+uXz9ckBnQ9k83Tr5nZ5tkPfTgwNT82DZiQzyeU0kVERPqUWi6Juhq4F1jXzOaY2VH51oG8f4DXtsAjeYnUz4Bj3b0YJPZp4IfAbOBp4JZMPw/Y2cyeIgL9eQ3sj4iISK/VYfO1ux/UTvon20i7gbhEqq3lZwIbtJH+N2CnjvIhIiKyqNOMXiIiIhWhoCwiIlIRCsoiIiIVoaAsIiJSEQrKIiIiFaGgLCIiUhEKyiIiIhWhoCwiIlIRCsoiIiIVoaAsIiJSEQrKIiIiFaGgLCIiUhEKyiIiIhWhoCwiIlIRCsoiIiIVoaAsIiJSEQrKIiIiFaGgLCIiUhEKyiIiIhWhoCwiIlIRCsoiIiIVoaAsIiJSEQrKIiIiFaGgLCIiUhEKyiIiIhWhoCwiIlIRHQZlM5tsZi+Z2WOltDPNbK6ZPZyP3UrvfdnMZpvZk2a2Syl9XKbNNrNTSulrmNkfMv1aM1uimTsoIiLSW9RSU/4JMK6N9G+7++h8TAcws/WAA4H18zPfN7N+ZtYPuBTYFVgPOCiXBTg/17U28CpwVCM7JCIi0lt1GJTd/W7glRrXNx64xt3fcPdngdnApvmY7e7PuPubwDXAeDMzYEfgZ/n5KcDendwHERGRRUIjfconmNkj2bw9ONOGA8+XlpmTae2lrwj83d3fbpXeJjObaGYzzWzm/PnzG8i6iIhI9dQblC8D1gJGA/OAC5uWo4Vw90nuPsbdxwwZMqQ7NikiItJt+tfzIXd/sXhuZpcDv8yXc4GRpUVHZBrtpP8NGGRm/bO2XF5eRESkT6mrpmxmw0ov9wGKkdnTgAPNbEkzWwMYBdwPPACMypHWSxCDwaa5uwN3Avvl5ycAU+vJk4iISG/XYU3ZzK4GtgdWMrM5wBnA9mY2GnDgOeAYAHd/3MyuA/4EvA0c7+7v5HpOAG4F+gGT3f3x3MTJwDVmdjbwEPCjpu2diIhIL9JhUHb3g9pIbjdwuvs5wDltpE8HpreR/gwxOltERKRP04xeIiIiFaGgLCIiUhEKyiIiIhWhoCwiIlIRCsoiIiIVoaAsIiJSEQrKIiIiFaGgLCIiUhEKyiIiIhWhoCwiIlIRCsoiIiIVoaAsIiJSEQrKIiIiFaGgLCIiUhEKyiIiIhWhoCwiIlIRCsoiIiIVoaAsIiJSEQrKIiIiFaGgLCIiUhEKyiIiIhWhoCwiIlIRCsoiIiIVoaAsIiJSEQrKIiIiFaGgLCIiUhEKyiIiIhXRYVA2s8lm9pKZPVZK+5aZ/dnMHjGzG81sUKavbmb/NrOH8/GD0mc2MbNHzWy2mV1sZpbpK5jZDDN7Kv8O7oodFRERqbpaaso/Aca1SpsBbODuGwJ/Ab5ceu9pdx+dj2NL6ZcBRwOj8lGs8xTgDncfBdyRr0VERPqcDoOyu98NvNIq7TZ3fztf3geMWNg6zGwYMNDd73N3B64A9s63xwNT8vmUUrqIiEif0ow+5SOBW0qv1zCzh8zsLjPbJtOGA3NKy8zJNICh7j4vn78ADG1vQ2Y20cxmmtnM+fPnNyHrIiIi1dFQUDazrwBvA1dm0jxgVXffCPg8cJWZDax1fVmL9oW8P8ndx7j7mCFDhjSQcxERkerpX+8HzeyTwB7AThlMcfc3gDfy+SwzexpYB5jLgk3cIzIN4EUzG+bu87KZ+6V68yQiItKb1VVTNrNxwJeAvdz9X6X0IWbWL5+vSQzoeiabp18zs81z1PXhwNT82DRgQj6fUEoXERHpUzqsKZvZ1cD2wEpmNgc4gxhtvSQwI69sui9HWm8LnGVmbwHvAse6ezFI7NPESO6liT7ooh/6POA6MzsK+Cuwf1P2TEREpJfpMCi7+0FtJP+onWVvAG5o572ZwAZtpP8N2KmjfIiIiCzqNKOXiIhIRSgoi4iIVISCsoiISEUoKIuIiFSEgrKIiEhFKCiLiIhUhIKyiIhIRSgoi4iIVISCsoiISEUoKIuIiFSEgrKIiEhFKCiLiIhUhIKyiIhIRSgoi4iIVISCsoiISEUoKIuIiFSEgrKIiEhFKCiLiIhUhIKyiIhIRSgoi4iIVISCsoiISEUoKIuIiFSEgrKIiEhFKCiLiIhUhIKyiIhIRSgoi4iIVISCsoiISEXUFJTNbLKZvWRmj5XSVjCzGWb2VP4dnOlmZheb2Wwze8TMNi59ZkIu/5SZTSilb2Jmj+ZnLjYza+ZOioiI9Aa11pR/AoxrlXYKcIe7jwLuyNcAuwKj8jERuAwiiANnAJsBmwJnFIE8l
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"tags": [],
"needs_background": "light"
}
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "q2IkKpXZzRiP"
},
"source": [
"## Exiting the bot\n",
"\n",
"Once you're done, you can call `bot.quit()` or `bot.end()` to disconnect and stop the bot."
]
},
{
"cell_type": "code",
"metadata": {
"id": "1-NxvPk1YuGw"
},
"source": [
"bot.quit()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "SpwlmlCBc90Q"
},
"source": [
"## Read more\n",
"\n",
"* **API** - https://github.com/PrismarineJS/mineflayer/blob/master/docs/api.md\n",
"* **Type Definitions** - https://github.com/PrismarineJS/mineflayer/blob/master/index.d.ts\n",
"* FAQ - https://github.com/PrismarineJS/mineflayer/blob/master/docs/FAQ.md\n",
"* JS tutorial - https://github.com/PrismarineJS/mineflayer/blob/master/docs/tutorial.md\n"
]
}
]
}