534 lines
38 KiB
Plaintext
534 lines
38 KiB
Plaintext
|
{
|
||
|
"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": "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
|
||
|
"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"
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
}
|