Minecraft is an open world game originally created by Markus “Notch” Persson. There is no set objective in Minecraft. Rather, players can interact with the game in ways that suit their interests. Activities a player can perform during play include (1) exploration of the world, (2) mining of various substances such as wood, iron, coal, and diamond, (3) gathering of food, (4) crafting, and (5) combat.
Minecraft has a client-server architecture. Specifically, a Minecraft world resides on a server, and players can connect to a Minecraft world using a client.
A typical way of playing Minecraft is to use the Minecraft launcher (the software you downloaded from Mojang) to join a Minecraft world. There are many options possible. For example, you can create your own world and join it (i.e., enter the world you created). In this case, both the server and the client will be running on your machine. It is also possible to connect your client to an online server.
CanaryMod is a Minecraft server that you can install on your machine. An interesting feature of CanaryMod is that it has a plugin API which allows its behavior to be customized. This ability to perform customization distinguishes CanaryMod from a standard vanilla Minecraft server.
Bricklayer is integrated with Minecraft through a CanaryMod plugin called RasberryJuice. This plugin is a PC implementation of the key functionality of the Minecraft: Pi Edition modding API (the Pi Edition is a free version of Minecraft developed by Mojang for the Rasberry Pi computer).
The RasberryJuice plugin provides a function, called setBlock, capable of placing a block at a particular position in a Minecraft world. In order for setBlock to work, the Minecraft world in which the block is to be placed must reside on the CanaryMod server. Another important RasberryJuice function is called setPos which enables a player to be placed at a particular location in a Minecraft world.
It is primarily through the RasberryJuice functions setBlock and setPos that Bricklayer interacts with a Minecraft world (residing on a CanaryMod server).
How It Works
In the discussion that follows, we assume an environment where CanaryMod is up and running and your Minecraft client has been connected to the CanaryMod server.
An artifact residing in Bricklayer’s virtual space can be built (i.e., exported) to a Minecraft world residing on a CanaryMod server by simply changing the show function call (which outputs the artifact to LDD) to showMC. The showMC will cause Bricklayer to create a Python program consisting of one setPos function call, to position your avatar (i.e., player), followed by a sequence of setBlock function calls, one function call for each cell in Bricklayer’s virtual space occupied by a piece. The resulting Python program is written to a file which is then executed using a system-level command. From a conceptual standpoint, that is all there is to it. The rest is engineering. In this case, it is software engineering.
In engineering, the realization of a conceptually simple solution to a problem can often times be much more complex than initially expected. In the workplace, this can result in tension between management, whose understanding of a problem is in conceptual terms, and engineers, whose constructions must account for all of the details underlying the conceptual solution. Engineers will tell you that, when building something, the vast majority of the work revolves around details of the solution that are either not initially known or not inherently apparent at the conceptual level. This is also the case when it comes to integrating Bricklayer with Minecraft. We will not go into the nitty-gritty details here, but would like to list some of the issues that Bricklayer must address.
- Documentation must be developed explaining how to install the tools necessary for integrating Bricklayer with Minecraft.
- Documentation must be developed listing the Minecraft blocks supported. To improve readability, it is useful for this documentation to associate a name and an image (e.g., gif) with each supported block. A plan must be developed for obtaining this set of images.
- The Python API (mcpi) specifically assigns names to 71 Minecraft blocks. From a technical standpoint, it turns out that the setBlock function can accept codes for over 343 blocks. However, some blocks cannot be placed. After obtaining the Minecraft block list (i.e., names and codes), a series of tests was conducted to determine which blocks can be placed. It turns out, that the double plant blocks (e.g., sunflower) cannot be placed using the setBlock function. When dealing with problems of this scale (and bigger) it is extremely important to have a systematic approach to organizing tests, running tests, and checking test results.
- Requirements Gathering
- In order for tests to produce reliable results, conditions must be created suitable for the placement of blocks (i.e., preconditions for the placement of blocks must be satisfied). For example, a melon stem must be placed on farmland and a lily pad must be placed on water. These conditions must be discovered and should be documented. It is important that the list of conditions be sound (i.e., correct) and complete (i.e., accounting for all supported blocks).
- Through testing it has been determined that timing as well as order is important to the placement of certain kinds of blocks. More specifically, some blocks have temporal and spacial dependencies associated with them. For example, doors should be placed last (after door frames). The setBlock function only places single blocks. However, a door occupies two blocks. This causes some problems with doors in general. However, testing suggests that it is best to place the bottom block of a door before placing its top block. The sand and gravel blocks will fall if there is air below them. When creating large artifacts in a Minecraft world situations can arise where there is enough delay to cause a sand block to fall if it is placed above and before another non-air block. To address this issue, blocks should be placed from lowest to highest.
- Use Cases
- Bricklayer must also anticipate how it will be used to create artifacts in Minecraft. Consider a scenario where, through Bricklayer, a large complex artifact has been created in a Minecraft world. Suppose that one wants to move a large complex artifact a few blocks to the right. Simply changing coordinates and calling showMC will create a duplicate artifact. In almost all cases, such a simple process will produce unacceptable results. The correct approach is to first erase the existing artifact and then re-build it at a new location. To support this type of activity Bricklayer provides function call showAsAir. Replacing showMC with showAsAir in a Bricklayer program will cause the artifact created by the program to be constructed entirely out of air blocks. This is acceptable in most (but not all) cases. Consider the case where a building is constructed underground and the substance above the building is sand or gravel. Currently, Bricklayer does not handle such cases.
- The most natural way to incorporate Minecraft blocks into Bricklayer is to extend the definition of the pieces that can occupy cells in Bricklayer’s virtual space. Instead of just allowing LEGO bricks to occupy cells, Minecraft blocks may also occupy cells. Thus, artifacts in the virtual space can be abstractly seen as a heterogeneous set of LEGO bricks and Minecraft blocks. This design decision has consequences for Bricklayer’s show, showMC, andshowAsAir functions. Artifacts viewed in LDD must consist exclusively of LEGO bricks and artifacts created in a Minecraft world must consist exclusively of Minecraft blocks. To satisfy this constraint a function needs to be developed that maps LEGO bricks to Minecraft blocks, and another function needs to be created that maps Minecraft blocks to LEGO bricks. The implementation of these functions must be tested (or verified) to assure that an attempt to place a LEGO brick in a Minecraft world will never be made. Similarly, a Minecraft block should never be sent to LDD. The show/showMC/showAsAir functions will use these mapping functions to translate the contents of each cell in Bricklayer’s virtual space so that the translated piece is appropriate for the tool being targeted. Since Bricklayer can create Minecraft artifacts consisting of hundreds of thousands of pieces. The efficiency of the mapping functions should be taken into account.
- It turns out that the coordinate spaces of Bricklayer and Minecraft do not align. In a typical navigational map North corresponds to “up” on the map and East corresponds to “right” on the map. In mathematics, an xz Cartesian coordinate plane drawn on a piece of paper will typically associate North (the top of the paper) with the positive z-axis and East with the positive x-axis. Bricklayer’s coordinate system is based on this model. However, Minecraft associates North with the negative z-axis and East with the positive x-axis. Because of this, Bricklayer needs to make adjustments to coordinates so that the orientation of an artifact viewed in LDD is the same as that of Minecraft. Furthermore, users of Bricklayer should be shielded from this coordinate discrepancy to the fullest extent possible.
Bricklayer’s Minecraft-centric Functions
|showMC : string → unit
|This function assumes the CanaryMod server is up an running and you have connected to the server using your Minecraft client. When called, the showMC function will output the contents of Bricklayer’s virtual space to Minecraft.
|showAsAir : string → unit
|This function assumes the CanaryMod server is up an running and you have connected to the server using your Minecraft client. When called, the showAsAir function will output the contents of Bricklayer’s virtual space to Minecraft. However, every non-empty cell in the virtual space will be mapped to the Air block. This function can be used to “erase” a Bricklayer artifact from a Minecraft world.
|setSpawnPoint : point → unit
|The spawn point is a constant integer-valued 3D coordinate in a Minecraft world associated with an avatar (i.e., a player). The third code-along discusses how to determine the spawn point for your avatar. In general, the spawn point is important for translating between the Bricklayer’s coordinate space and the coordinate space of a Minecraft world. The setSpawnPoint function is used to make Bricklayer aware of your avatar’s spawn point.
|setMinecraftOrigin : point → unit
|Provided the spawn point has been set, Bricklayer has the ability to treat any coordinate in Minecraft world as the origin, which we call the minecraftOrigin. By default, Bricklayer assumes that the minecraftOrigin corresponds to the origin in Bricklayer’s virtual space (i.e., (0,0,0)). The function setMinecraftOrigin is used to set the location of the minecraftOrigin in Bricklayer.
|setBricklayerOrigin : point → unit
|By default, Bricklayer assumes that the minecraftOrigin corresponds to the origin in Bricklayer’s virtual space (i.e., (0,0,0)). The function setBricklayerOrigin is used to move, in the Minecraft world, the position that Bricklayer will consider to be the origin of its virtual space. We refer to the Bricklayer origin in a Minecraft world as bricklayerOrigin. Operationally, the bricklayerOrigin is specified in relative terms, as an offset from the the minecraftOrigin. This offset approach shields the user from awkward Bricklayer-Minecraft coordinate translations.
|setPlayerOffset : point → unit
|Bricklayer allows control of where an avatar (i.e., a player) is placed in a Minecraft world. The function setPlayerOffset takes an 3-dimensional offset as its input and places the avatar at a distance corresponding to the offset away from the bricklayerOrigin.
|setSuppressOrientationMarker : bool → unit
|By default, Bricklayer positions an avatar on top of an orientation marker which indicates the direction Bricklayer considers to be the positive x-axis (i.e., East) and the positive z-axix (i.e., North). The orientation marker consists of Glowstone and Iron blocks. However, as the positioning of an artifact stabilizes, the orientation marker becomes less useful. Furthermore, if multiple artifacts are placed in a Minecraft world, it is common to encounter situations where multiple markers are created. The setSupressOrientationMarker disables the creation of the orientation marker.
|setPieceMapping : (piece * piece) list → unit
|The setPieceMapping function allows programmers to customize how Bricklayer translates LEGO bricks to Minecraft blocks and vice versa.
Known Issues and Limitations
Bricklayer currently supports 343 Minecraft blocks. However, there are problems and limitations associated with the placement of some blocks. The full range of possibilities of how blocks can be used has not yet been tested. However, the known issues and limitations discovered thus far are listed below.
|In Minecraft, a bed has a length of two blocks. However, RaserryJuice’s setBlock function only supports the placement of single block items. For this reason, it is not (currently) possible to construct functioning beds using Bricklayer.
|In Bricklayer, levers cannot be reliably placed on the side of a block.
|In Bricklayer, a Torch can only be placed on the top of a block. It cannot be placed on the side of a block.
|OakDoor, SpruceDoor, BirchDoor,
JungleDoor, AcaciaDoor, DarkOakDoor
|In Minecraft, a standard wooden door is 2 blocks tall. However, RaserryJuice’s setBlock function only supports the placement of wooden doors that are 1 block tall. This makes the construction of wooden doors problematic. It is also difficult to open and close these doors using right mouse clicks. Rebuilding an artifact (e.g., by runing a Bricklayer program a second time) can cause the placement of doors to fail resulting in a door icon suspended in the air. Doors also cannot be erased by overwriting their location with an Air block.
|In Minecraft, an iron door is 2 blocks tall. However, RaserryJuice’s setBlock function only supports the placement of iron doors that are 1 block tall. This makes the construction of iron doors problematic. Furthermore, in Minecraft, opening an iron door requires a mechanism other than a right mouse click. For example, a RedstoneWire can be combined with RedstoneTorch or a DaylightSensor to create a mechanism for opening/closing an iron door. Pressure plates (e.g., StonePressurePlate) and levers can also be used. We have not tested all the possibilities, but have not had much success with the tests we have run.
|In Minecraft, Sand and Gravel are blocks that fall through the air. For this reason, Bricklayer constructs artifacts bottom up (i.e., from the ground up). However, when constructing an artifact containing blocks of Air (especially large artifacts) Sand/Gravel blocks may fall into the air spaces of the artifact. For example, to excavate (e.g., remove a portion of the ground) one places a rectangular prism made of Air blocks at a location that is currently occupied by non-Air blocks (e.g., Stone, Dirt, Grass, Sand, and Gravel). In Bricklayer, this excavation takes place in a bottom up fashion. First, the lowest non-Air blocks are replaced by Air blocks, and so on. Depending a variety of factors such as the size of the area being excavated, it is possible for Sand/Gravel blocks to fall into the air spaces that are being created below them. One way to get round any problems created by such falling blocks is to run your excavation program twice. We have not encountered a situation where more than two runs are needed to resolve any issues.