Modules and Packages#

In this tutorial we will start to use vscode to develop a Python package for making buildings in the game. We will be starting with the pagoda building from the previous tutorial and here we will also go into the detail of how the build_pagoda function works.

You may like to install Python extensions for vscode at this point, see VSCode Extensions

Getting a Bash Prompt#

We are going to use some bash commands here so lets open a second terminal in VSCode so that we can have a bash prompt but also keep our Python shell prompt open.

Right click in the VSCode terminal area and select Split Terminal

split_terminal

Splitting the Terminal#

You can grab the dividing line between the two terminals and drag it to the right with your mouse. This makes the Python terminal bigger and the Bash terminal smaller. Then your terminal area of VSCode should look like this:

split_terminal

Python prompt and Bash prompt#

Create a Package and a Module#

A python module is simply a file with a .py extension containing Python code.

A package is simply a folder (directory) that contains python modules.

We are going to create a package called buildings with a single module to start with called build_pagoda. Type the following commands at the bash prompt:

cd $HOME/my_world
mkdir buildings
cd buildings
touch __init__.py
code pagoda.py

The steps above were:

  • Change working directory to $HOME/my_world which is our VSCode work folder.

  • Make a new directory called buildings, this is our new package

  • Change working directory to buildings

  • Create an empty file called __init__.py in the buildings directory. This is a special file name that tells Python that this directory is a Python package.

  • Tell vscode to open a new file called pagoda.py in the editor window

Now we can paste our pagoda function into the editor window and save it with Menu -> File -> Save (or Ctrl+S is a shortcut to save the current file). Use this slightly modified version of the pagoda function:

from mciwb.imports import Direction, FillMode, Item, get_client, polygon


def build_pagoda(pos, width=4, floor_height=4, item: Item = Item.GOLD_BLOCK):
    """
    Create a pagoda out of blocks at the given position.

    Each successive floor is narrower and each has a balcony at the top.

    :param pos: position of the base of the pagoda
    :param width: width of the base of the pagoda
    :param floor_height: height of each floor
    :param item: block to use for the base of the pagoda
    """
    c = get_client()

    # calculate how many levels we can make if we reduce width by 2 each level
    levels = width // 2

    for level in range(levels):
        # calculate the width of the pagoda for this level
        floor_width = width - 2 * level

        # calculate the base position of the walls for this level
        base = pos + Direction.UP * level * floor_height

        # calculate the position of the balcony for this level
        # it should surround the top layer of the walls for this level
        balcony = base + Direction.UP * (floor_height - 1)

        # create the balcony for this level
        polygon(
            client=c,
            center=balcony,
            height=1,
            diameter=floor_width + 2,
            sides=4,
            item=item,
            mode=FillMode.REPLACE,
        )

        # create the walls for this level
        polygon(
            client=c,
            center=base,
            height=floor_height,
            diameter=floor_width,
            sides=4,
            item=item,
            mode=FillMode.REPLACE,
        )

To try using this function you can now type the following command in the iPython prompt:

from buildings.pagoda import build_pagoda
build_pagoda(world.player.pos)

That should build a little pagoda around your player. You can break some blocks to make a door for you to exit the pagoda!

How it Works#

Let’s take a look at all of the new things that we used in our build_pagoda function.

Import#

from mciwb.imports import Direction, Item, get_client, polygon

import allows us to access code from other modules.

One of the greatest features of Python is its extensive library of built in modules. For example the maths module contains functions for doing math. e.g.

from math import sqrt, cos, sin

The polygon function that we used to make the pagoda is implemented using some of these math functions. Luckily, you don’t need to know anything about the mathematics of polygon construction because the polygon function has done all that for you!

So, Direction and Item are already familiar, we have previously used them in iPython without needing to import them. But we need to import them here because we are writing a new module called pagoda (in the file pagoda.py). import is the command to share code between modules and is therefore required in our new pagoda module.

import is usually used at the beginning of a module and code that is imported is available to all of the code within the module.

Once you become familiar with Python you will eventually be comfortable looking at online documentation to discover packages and modules you want to use and discovering the imports you need to use them. But for now it is easiest just to copy the import statements from examples like pagoda.py.

Comments#

At the beginning of build_pagoda we have a block comment. It uses triple quotes """ which allow you to write a block of text including line breaks that is not interpreted as Python code.

Good programmers will usually add a comment block at the top of their functions and using triple quotes is the standard way to do this.

Get_client#

Whenever we call any functions that make changes to Minecraft we need a client object. This represents a connection to our Minecraft server. (An exception to this is functions in the world object since world holds a client connection for us to save on typing)

The get_client function obtains a client object for you to use. Here we assign it into the variable c and pass c to the polygon function.

Advanced programmers may want to read up on how this is a thread-safe client object! see (MCIPC Client object).

Polygon#

polygon is a function that draws the outline of a polygon using blocks. It is capable of making any regular polygon and using that shape to build a tower. The parameter height tells it how many levels of the same polygon to stack on top of each other. If you want to make a circular tower then you can just pass a large number like 400 as the number of sides. In pagoda we use 4 sides to make a square pagoda.

Making a Pagoda#

So how does the overall function work?

The caller has specified the width of the bottom of the pagoda. We use this to calculate how many levels we can make. Each level is 2 blocks narrower than the one below and so width divided by 2 should be the number of levels. We use integer division to round down to the nearest integer as we don’t want to try to make half a level // is the integer division operator.

Next we see that we loop over the range of levels. For our width 30 pagoda we have 15 levels and therefore level will range from 0 to 14.

For each iteration of the for loop we build some walls and a balcony.

The base of the walls is calculated as level * floor_height blocks above the starting pos. floor_height is the default value of 4. Thus, the first floor base is 0 blocks above the starting pos (0 times 4) and each successive level is 4 blocks above the previous level.

polygon(
    client=c,
    center=base,
    height=floor_height,
    diameter=floor_width,
    sides=4,
    item=item,
)
To create the walls we call polygon with these parameters:
client is set to c:

the client object needed to talk to the Minecraft server

centre is set to base:

the calculated starting point for the polygon

height is set to floor_height:

the height of the polygon (defaulted to 4)

diameter is set to floor_width:

the diameter of the polygon (starting at 30 for the bottom level)

sides set to 4:

the number of sides of the polygon, we use 4, making a square

item is set to item(which was passed to build_pagoda):

the blocks to use for the polygon, defaulted to GOLD_BLOCK

mode is set to FIllMode.REPLACE

this means that all pagoda blocks replace existing blocks. You could also use FillMode.KEEP which only places blocks if the current block is Item.AIR.

As we loop around the for loop we create a new polygon at each level, but the width of it is shrinking by two blocks at each level. Eventually we get to a narrow level at the top and the loop completes.

Exercise:

Can you work out how the balcony is being drawn on each level? look at the math used to calculate balcony and the polygon function parameters used to draw the balcony and see if you can figure out how it works.

Exercise 2:

I found pesky pillagers patrolling on my pagoda. A friend pointed out that you can avoid this by placing slabs on top of every surface of the pagoda. Mobs (mobile entities) won’t spawn on a slab. Also you can make the slab a different material for an interesting look. You can make a pagoda with slabs on its surfaces with just two calls to the pagoda function. Can you work out how?