Minecraft has an API. If you run your own server you can program it from Python. Here are notes from how I set it up. It’s a lot of fun to make things happen in Minecraft with Python.
Basic server setup
I used a Digital Ocean 2GB single CPU instance, which seems plenty, with Ubuntu 20.04. There are cheaper hosting providers. Do some basic setup, initially as
apt-get update apt-get upgrade adduser <your-username> usermod -aG sudo <your-username> # on Ubuntu the `sudo` group gives you sudo permissions
Put your ssh key in
Allow yourself to sudo without password:
sudo visduo add
NOPASSWD: to the
sudo group so it looks like this:
%sudo ALL=(ALL:ALL) NOPASSWD: ALL
Check you can login as the user you just created. If that works edit
/etc/ssh/sshd_config to disable root login and make sure password authentication is already disabled.
Setup and Run the Minecraft server
Open the port Minecraft server listens on, and enable the firewall:
ufw allow 25565 ufw enable
Later we’ll add the API server which listens on port 4711, and you don’t want the whole Internet scripting your Minecraft server. Hence the firewall.
Minecraft is Java so install that:
sudo apt install openjdk-17-jre-headless
Instead of running the official Minecraft server, we’re going to run a variant called Paper MC. It’s very popular, faster than the offical build, and supports a wider range of plugins.
Download latest Paper MC JAR, make a directory
and drop it in there.
Don’t run it from your home directory because it will create a bunch of files after it starts. I used the latest 1.18, but possibly the 1.17 would have been wiser as some plugins weren’t updated yet. Depends if there’s some specific plugins you want (if you don’t know what those are then you’re fine).
Start minecraft server:
/usr/bin/java -Xmx1536M -Xms1536M -jar Paper<version>.jar nogui
Adjust those Xmx / Xms values if you have more of less memory on your server; these are about right for a 2GB instance.
It will probably ask you to accept the EULA, so do that by editing the
eula.txt it created to read
eula=true. Don’t start the server again yet. Optionally also edit
server.properties and change
motd (message of the day) to something fun.
Add the API
The API seems to have two parts: a server part and a python library to talk to it. The server part is called Raspberry Juice (because it came from the Raspberry PI device).
Download the most recent JAR from https://github.com/zhuowei/RaspberryJuice/.
I have 1.11 (but I should probably have 1.12.1).
The client part is mcpi. Make sure you have pip (Python package manager) then install the library:
sudo apt install pip3 pip3 install mcpi
Start the server again. RaspberryJuice should create it’s directory. Stop the server.
In the Raspberry PI version of Minecraft co-ordinates are relative to the player’s spawn point, and that’s what RaspberryJuice uses by default. In Minecraft server they are not, they are absolute, so we want the API to match. Edit
minecraft-server/plugins/RaspberryJuice/config.yml and change location to read
Start Minecraft more permanently
We want Minecraft to keep running when you disconnect from the server. The correct way to do this is with
systemd. Most people use
screen. I used
tmux, it works well, but do whatever you’re comfortable with. Instal tmux and start it:
sudo apt install tmux tmux
and run Minecraft in there (the
java line earlier).
Now detach from tmux by pressing Ctrl-b then d (by itself, not Ctrl-d). The server will continue running after you disconnect from ssh.
Connect and whitelist
Digital Ocean should tell you your server’s IP address. Start the Minecraft client, go to Multiplayer / Add Server and join that. To make your server more private whitelist your players' usernames after you connect. In chat type:
/whitelist add <your-minecraft-username> # for each of your players including yourself /whitelist on
If you mess it up edit
whitelist.json on the server.
Your server is setup! You can play, which sure that’s fun, but we want to code!
On the server run
python3 to get the Python command line. Import the library:
from mcpi.minecraft import Minecraft import mcpi.block as block
mc = Minecraft.create()
Send a message in the chat:
Find a list of player IDs:
Find and save your player’s position - this should match what you see when you press F3 in minecraft client:
x, y, z = mc.entity.getPos(230) # The player entity ID from above
Create a block near your player:
mc.setBlock(x+1, y+1, z+1, block.DIRT)
Some of the blocks are defined in
block, but for most of them you need to know their ID. I don’t think they will all work, we’re kinda hacking things here none of this is official. Some blocks have a state in which case I think you pass the id then the variant as the next parameter.
You can explore the API by typing
mc.<Tab> in the Python client. It’s also documented on the github page and the API reference in depth is here.
An alternative to mcpi that I haven’t tried is picraft.
I got into all this in the first place because I bought my children Learn to Program with Minecraft. It could use an update, but it’s definitely a solid choice. Lots and lots of examples there as it gradually teaches programming.
Happy Minecraft scripting!
PS: as a bonus here’s a Python script that flattens an area around the player (to e.g. build a farm). The pauses are unnecessary but make it more fun to watch.
import time import math from mcpi.minecraft import Minecraft import mcpi.block as block def clear_pillar(lx, ly, lz): id = mc.getBlock(lx,ly,lz) while id != 0: mc.setBlock(lx,ly,lz, block.AIR) ly += 1 id = mc.getBlock(lx,ly,lz) time.sleep(PAUSE) PAUSE = 0.02 WIDTH = 5 HEIGHT = 10 mc = Minecraft.create() start_x, start_y, start_z = mc.entity.getPos(230) print(start_x, start_y, start_z) x, y, z = math.floor(start_x), math.floor(start_y), math.floor(start_z) def dirt_below(): for xx in range(x-WIDTH, x+WIDTH): for zz in range(z-WIDTH, z+WIDTH): for yy in range(y-HEIGHT, y): mc.setBlock(xx, yy, zz, block.DIRT) time.sleep(PAUSE) def air_above(): for xx in range(x-WIDTH, x+WIDTH): for zz in range(z-WIDTH, z+WIDTH): for yy in range(y, y+HEIGHT): mc.setBlock(xx, yy, zz, block.AIR) time.sleep(PAUSE) air_above() dirt_below()
This post was featured in PyCoder’s Weekly #504