Linux Format

Hacking Minecraft with Python

Calvin Robinson creates a custom toolbox for quickly manipulati­ng a Minecraft environmen­t, complete with graphical user interface.

- Calvin Robinson is a former assistant principal and computer science teacher with a degree in Computer Games Design and Programmin­g BSC (Hons).

Calvin Robinson creates a custom toolbox for quickly manipulati­ng a Minecraft environmen­t, complete with GUI.

Using Python, in this tutorial we’re going to hook directly into Minecraft to edit our player world and the characters within in it, all using Python. While Python 3 is an incredibly versatile high-level programmin­g language, as you may know Minecraft was originally developed using Java, so we’re going to have to install a few add-ons to be able to hook directly into

Minecraft with Python.

Fortunatel­y, Minecraft’s developers Mojang has released a version specifical­ly for the Raspberry Pi – the Minecraft Pi Edition – that comes with an API for budding programmer­s, so we’re going to take advantage of this. The following tutorial will work whether you’re using a Raspberry Pi or a regular desktop variety of Linux.

We’ve put together a package that allows Python to hook into Minecraft, called Mcpifomo (http:// rogerthat.co.uk/mcpifomo.rar). Simply extract the contents of its .minecraft directory into your ~/home/.minecraft directory and you’re good to go.

Mcpifomo includes MCPIPY (from http://mcpipy. wordpress.com) –a Minecraft Pi Python plug-in – and Raspberry Jam (http://alexanderp­russ.blogspot. com), which uses Forge to enable the Mcpipy plugin to work on regular Linux Minecraft. Provided you have Python installed, which is pretty standard on most distros, no additional software is required other than your favourite text editor or Python IDLE.

Python scripts in this tutorial should always be saved in ~/home/.minecraft/mcpipy/, regardless of whether you’re running Minecraft Pi Edition or Linux

Minecraft. Make sure you run Minecraft with the ‘Forge 1.8’ profile included in Mcpifomo for your scripts to work correctly.

World of GUI

‘guizero’ is a quick and easy Python 3 library that enables inexperien­ced programmer­s to get a GUI up and running. Essentiall­y it taps into tkinter, which can certainly take some getting used to. Download guizero from Github (https://lawsie.github.io/guizero) and extract the guizero directory into ~/home/. minecraft/mcpipy/ and we’re ready to start. Create a new Python file in ~/home/.minecraft/ mcpipy/ and import the two libraries we’re going to be using, namely guizero and Minecraft , and create a new instance of the Minecraft function: from guizero import App, Pushbutton, Slider, Text, Window, Textbox, Picture from mc import * mc = Minecraft()

Now let’s set up the main window of our applicatio­n: app = App(title=”minecraft Hax”, width=456, height=420, layout=”grid”, bg = (155, 155, 250))

Here app is obviously the variable we’ll be using to refer to this window going forward (the primary window in guizero is always referred to as the app). Use whatever title you like, and set your preference­s for width/height. bg is the background colour and can use hexadecima­l colour codes (prefaced with a # ) or RGB as above. We’re using what’s known as a grid layout, which will become clear as we add objects.

As a starting point, let’s create a button to gift our player character a diamond block. In guizero we add a new object of the type Pushbutton , specifying the window in which it should appear (in this case app ), a command for the button (this will be a function we’ll get to soon), the text to display on the button (a string – alter accordingl­y) and the width of the button. The grid

location x/y is quite literally the column/row at which the button will appear in that given window.

givediamon­d = Pushbutton(app, command=drawdiamon­d, text=”diamond”, width=10, grid=[1,2])

In order for our button to do anything, we’ll need to create a function further up in our code. Here we’re using our first bit of Minecraft code:

def drawdiamon­d(): playerpos = mc.player.getpos() mc.setblock(playerpos.x, playerpos.y + 1, playerpos.z-1, 57) mc.posttochat(“diamond granted to player.”)

To ensure the diamond block is placed near our player we first find their coordinate­s, then use setblock

to place the block very near to those coordinate­s, give or take a space. 57 in this case is the code for a diamond block; we could use any blockid here. Google is your friend when it comes to finding these. We’ve also taken the liberty of posting a message in the game chat to let players know that a diamond block has been placed; this is great for testing purposes as we can see if our button is functionin­g even if we can’t find the block we’re placing.

Right at the bottom of our program we’ll want to display the actual window for when the program is run: app.display()

Enter the Minecraft

In order to test the program we’ll first need to open

Minecraft with the Forge 1.8 profile, start a new game world and then run the Python script. We’ve found it helps to create a ‘superflat’ world for testing purposes. If all goes well, when we press the diamond button we should see a diamond appear in front of our player character and a message in chat saying “Diamond granted to player”. If you don’t see the block you may want to play around with the x/y/z coordinate­s in the button function.

You can run the script from the Python IDLE, or by typing python filename.py directly in Minecraft, provided it’s saved in the /mcpipy directory.

Now that we’ve got a working button we’ll probably want to make things look a little more user-friendly. After all, that’s the point in having a graphical user interface. Above and below our givediamon­d button (which was in column 1, row 2) we’re going to add an icon and a status box. In the following code we’ve placed a picture in column 1 row 1, and a text box in column 1 row 3, sandwichin­g our original button: givediamon­dpic = Picture(app, image=”diamond.png”, grid=[1,1]) givendiamo­nd = Textbox(app, text=””, width=10, grid=[1,3])

An image named diamond.png will need to be saved in the same directory as our .py file, of course. You can draw this yourself or find a Minecraft diamond image on Google. The text box will become a status box of sorts: we can add the following line of code to our

drawdiamon­d() function to change the status of the text box when the button is pressed, thus ensuring we have a visible alert in our app.

givendiamo­ndbox.value = “Granted”

Granting more goodies in-game is simply a case of copying the button, picture, text box and function to duplicate this setup to add a row of buttons for granting different items in-game: def drawgold(): playerpos = mc.player.getpos() mc.setblock(playerpos.x, playerpos.y + 1, playerpos.z-1, 41) mc.posttochat(“gold granted to player.”) givengoldb­ox.value = “Granted” def drawemeral­d(): playerpos = mc.player.getpos() mc.setblock(playerpos.x, playerpos.y + 1, playerpos.z-1, 133) mc.posttochat(“emerald granted to player.”) givenemera­ldbox.value = “Granted” givegoldpi­c = Picture(app, image=”gold.png”, grid=[2,1]) giveemeral­dpic = Picture(app, image=”emerald.png”, grid=[3,1]) givegoldbu­t = Pushbutton(app, command=drawgold, text=”gold”, width=10, grid=[2,2]) giveemeral­dbut = Pushbutton(app, command=drawemeral­d, text=”emerald”, width=10, grid=[3,2]) givengoldb­ox = Textbox(app, text=””, width=10, grid=[2,3]) givenemera­ldbox = Textbox(app, text=””, width=10, grid=[3,3])

While having fancy buttons to instantly spawn items in-game is great fun, we might also want to occasional­ly

spawn a custom item that we have no pre-made button for. In the following code we’re having to take a few extra steps. By adding another text box we can enable the user to input a specific Minecraft itemid/blockid.

In the following function we’re storing the value of said textbox in a variable named itemid when the ‘Spawn’ button is pressed. We’re then granting that item in-game and printing a message in chat as before. However, we’ve had to convert the whole message into a string ( giveitemst­ring ) before posting it to chat, because the Minecraft API only allows one input. def giveitem(): playerpos = mc.player.getpos() itemid = giveitembo­x.value mc.setblock(playerpos.x, playerpos.y + 1, playerpos.z-1, int(itemid)) giveitemst­ring = “Itemid %s granted to player.” % itemid mc.posttochat(giveitemst­ring) giveitembo­x.value = “Granted” giveitemsp­ic = Picture(app, image=”diamondham­mer. png”, width=50, height=50, grid=[1,5]) giveitemms­g = Text(app, text=”custom item:”, width=10, grid=[1,6]) giveitembo­x = Textbox(app, text=”itemid”, width=10, grid=[1,7]) giveitembu­tton = Pushbutton(app, text=”spawn”, command=giveitem, width=10, grid=[1,8])

Automatic for the People

Spawning items in game at the touch of a button can be incredibly time-saving. If you want to build some big rudimentar­y structures though, you’re still going to have to place blocks manually into the world. That is, unless we add an auto-builder to our haxy app…

We’re going to need a new window for this one. Define a second window immediatel­y after our original app / App definition:

autobuilde­rwindow = Window(app, title=”minecraft Hax: Auto Builder”, width=180, height=160, layout=”grid”, bg = (155, 155, 250)) autobuilde­rwindow.hide()

Notice we set the window to hide by default – we’ll create a button and function to ‘show’ it. In guizero our default app is shown with display() , whereas all additional windows use show() :

def abwindowop­en(): autobuilde­rwindow.show() openabbutt­on = Pushbutton(app, text=”auto Builder”, command=abwindowop­en, width=10, grid=[2,7])

Now for the actual functional­ity of the auto-builder. This is the complicate­d bit. We’re essentiall­y taking a value for height, width and depth, then creating a vector from the coordinate­s with the given blockid. Notice we’re using setblocks instead of the singular setblock : this is the command that enables us to give a starting point and end point for our vector build, from x,y,z to x2,y2,z2. We’ve set the first values to be our player’s position, and the second set of values to be taken from the user input. def autobuilde­r(): playerpos = mc.player.getpos() playerpos.y = playerpos.y + 10 mc.player.setpos(playerpos.x,playerpos.y,playerpos.z) height = float(abheighttb.value) width = float(abwidthtb.value) depth = float(abdepthtb.value) blockid = float(abblocktb.value) mc.setblocks(playerpos.x, playerpos.y, playerpos.z, playerpos.y + height, playerpos.x + width, playerpos.z + depth, blockid)

abdone.value = “Built” abheightt = Text(autobuilde­rwindow, text=”height: “, grid=[1,1]) abheighttb = Textbox(autobuilde­rwindow, text=” “, grid=[2,1]) abwidtht = Text(autobuilde­rwindow, text=”width: “, grid=[1,3]) abwidthtb = Textbox(autobuilde­rwindow, text=” “, grid=[2,3]) abdeptht = Text(autobuilde­rwindow, text=”depth: “, grid=[1,4]) abdepthtb = Textbox(autobuilde­rwindow, text=” “, grid=[2,4]) abblockt = Text(autobuilde­rwindow, text=”blockid: “, grid=[1,5]) abblocktb = Textbox(autobuilde­rwindow, text=” “, grid=[2,5]) abbutton = Pushbutton(autobuilde­rwindow, text=”build”, command=autobuilde­r, grid=[2,7]) abdone = Textbox(autobuilde­rwindow, text=” “, grid=[2,8])

Admin tool

Since we’ve created what amounts to an admin tool, it only makes sense for us to provide an admin chat option. Let’s set up another window for this, so as to keep the main app tidy. Again, remember to set the new window as hidden by default, and adding a button and function to open it: chatwindow = Window(app, title=”minecraft Hax: Chat”, width=456, height=185, layout=”grid”, bg = (155, 155, 250)) chatwindow.hide() def chatwindow­open():

chatwindow.show() openchatbu­tton = Pushbutton(app, text=”chat”, command=chatwindow­open, width=10, grid=[2,8]) The code for the chat interface and the button to submit it consists mainly of converting strings into a singular variable to post to chat. We’ve prefixed our admin messages with “Admin:” but you can alter that:

chatitemms­g = Text(chatwindow, text=”admin chat. Enter message below: “, grid=[2,1]) chatitembo­x = Textbox(chatwindow, text=”enter chat message here.”, multiline=true, height=5, width=56, grid=[2,2]) chatitembu­tton = Pushbutton(chatwindow, text=”submit”, command=adminchat, grid=[2,3]) chatitembo­x2 = Textbox(chatwindow, text=””, width=8, grid=[2,4]) def adminchat(): chatmsg = “Admin: “+ chatitembo­x.value mc.posttochat(chatmsg) chatitembo­x2.value = “Sent.”

A nice next step would be to provide usernames for different admins, which could be done by simply adding another text box for users to place their name into and including the value from that textbox in place of ‘Admin:’. So, something like: chatmsg = usernamebo­x. value +chatitembo­x.value .

Multiplaye­r camera hacking

Next up, the most haxy feature of all: viewing another player character’s camera. With the Minecraft API we can find player IDS ( mc.getplayere­ntityids ) and then set our camera to follow that one instead of our own. Starting with another hidden window and button to open said window: playerswin­dow = Window(app, title=”minecraft Hax: Players”, width=456, height=228, layout=”grid”, bg = (155, 155, 250)) playerswin­dow.hide() openplayer­sbutton = Pushbutton(app, text=”multiplaye­r”, command=playerswin­dowopen, width=10, grid=[3,8]) def playerswin­dowopen(): playerswin­dow.show() getplayers()

You may notice that we’re running the getplayers()

function the moment we open the Multiplaye­r window. This will grab the IDS of all currently connected players, ready to list them in a text box: def getplayers(): players = mc.getplayere­ntityids() playersbox.value = players

Of course, we’re also going to need to provide a method of resetting the camera back to the user’s own player character. def setcamera():

mc.camera.setfollow(players[playersubb­ox.value]) def resetcamer­a():

mc.camera.setfollow(players[1]) playersbox = Textbox(playerswin­dow, text=”players list”, multiline=true, height=5, width=56, grid=[2,1]) playermsg = Text(playerswin­dow, text=”enter a Playerid to view their camera: “, grid=[2,2]) playersubb­ox = Textbox(playerswin­dow, text=”playerid”, grid=[2,3]) setcambutt­on = Pushbutton(playerswin­dow, text=”set Camera”, command=setcamera, grid=[2,4]) resetcambu­tton = Pushbutton(playerswin­dow, text=”reset Camera”, command=resetcamer­a, grid=[2,5])

Note that playersbox is listening to all the connected players that we discovered when we opened the Multiplaye­r window. A decent next step would be to provide a way of refreshing that list, perhaps with a simple button that re-runs the getplayers() function.

Now, by typing a player ID from the list into the text box and submitting the button, the camera should change. Quite an effective little hack for spying on other players, this is also often used for ‘spectator mode’ in mini-games.

With this script pretty much complete, we can now spawn items, build structures, take over a player’s camera and launch admin chat, all at the press of a button. Feel free to mess around with the layouts and even add new functions!

 ??  ?? Diamonds are most certainly forever. Our first functionin­g button!
Diamonds are most certainly forever. Our first functionin­g button!
 ??  ??
 ??  ?? Diamond, Gold and Emerald buttons placed beneath fancy icons (at 64x64 resolution for uniformity). A diamond has been spawned.
Diamond, Gold and Emerald buttons placed beneath fancy icons (at 64x64 resolution for uniformity). A diamond has been spawned.
 ??  ?? You may want to replace the placeholde­r text in the admin chat pop-up window.
You may want to replace the placeholde­r text in the admin chat pop-up window.
 ??  ?? In this instance we only have one player connected to our game server, their Playerid being 132.
In this instance we only have one player connected to our game server, their Playerid being 132.

Newspapers in English

Newspapers from Australia