Skip to main content

Terraforming 2D Minecraft Structures

·1559 words·8 mins
Terraform Minecraft

A while back I wrote a popular blog post on how to set up developer self-service for Minecraft using HCP Waypoint as an internal developer portal (IDP).

Developer Self-Service with HCP Waypoint and Minecraft
·4354 words·21 mins
Hcp Waypoint Minecraft Terraform

As part of that blog post and the accompanying GitHub-repository I created structures in the Minecraft world using Terraform. I had Terraform modules for a house, a bridge, various HashiCorp product logos, and a pyramid. To compute the coordinates where to place blocks for these structures I used various techniques - from manually computing them using pen and paper to using nested for-loops in Terraform local values. For the HashiCorp product logos I asked ChatGPT to convert from logo (png) to Minecraft coordinates (with varying results).

In this blog post I want to define these coordinates in a different way.

First a note on coordinates in the Minecraft world:

A block in Minecraft is placed at a coordinate (x,y,z). The horizontal plane is made up of the (x,z) coordinate pair. The y-coordinate is the vertical coordinate, increasing up towards the sky and decreasing down towards the bedrock.

Creating an input language for Minecraft structures
#

Ideally, I would like to have an input file where I can define arbitrary 3D structures in a Minecraft world.

Spoiler alert ⚠️ I will not get to that point in this blog post. This needs further thought and work!

The idea would be to have something equivalent to a Terraform configuration file, where I can declaratively define the structure I want to create in Minecraft.

One way of doing this would be to actually use Terraform by developing the Minecraft provider for Terraform further to include new resource types such as a house, a bridge, and so on. This would be an entirely other beast to take on and it is not really what I had in mind right now.

Instead I want to start with a simple approach: creating 2D structures simply by drawing them in a text file.

For example, drawing a series of squares separated by an empty space would look like this:

xxxx  xxxx  xxxx  xxxx
x  x  x  x  x  x  x  x
x  x  x  x  x  x  x  x
xxxx  xxxx  xxxx  xxxx

Drawing a small Terraform logo would look something like this (I used this page to draw it):

xxxx                
xxxxxxx             
xxxxxxxxxx       xxx
xxxxxxxxxxxxxxxxxxxx
   xxxxxxxxxxxxxxxxx
       xxxxxxxxxxxxx
       xxxxxxxxxx   
       xxxxxx       
       xxxxxx       
       xxxxxx       
         xxxx       

In the example in the next section I will draw a bigger Terraform logo that looks more like the actual Terraform logo. I used a small version of the logo here just for illustrative purposes.

In the two examples above I use simple x characters to mark where a block should be placed. The absence of a block means that no block should be placed. Each x represents one block at one specific coordinate.

We can take this one step further by allowing different characters to mean different types of blocks. For instance, let’s say we will only use wool blocks for now. We could use a b for a blue wool block, r for a red wool block, y for a yellow wool block, and so on.

As an example, the following would draw a red square, followed by a green square, followed by a blue square:

rrrr  gggg  bbbb
r  r  g  g  b  b
r  r  g  g  b  b
rrrr  gggg  bbbb

This will be my Minecraft input language for Terraform v1.0!

Minecraft input language for Terraform v1.0

In this simple language characters in a 2D layout in a textfile represents blocks of different material in Minecraft.

Each column in the file represents different x-coordinates, and each row in the file represents different y-coordinates.

The 2D structures thus live in the (x,y)-plane with a fixed z-coordinate.

All coordinates are relative to an origin-coordinate that has to be supplied along with the structure to be created.

Implementation
#

As in my previous post on Minecraft and Terraform I use the HashiCraft Minecraft provider:

terraform {
  required_providers {
    minecraft = {
      source  = "HashiCraft/minecraft"
      version = "0.1.1"
    }
  }
}

provider "minecraft" {
  address  = var.address
  password = var.password
}

I will not go through the details of how to create a working Minecraft server. Check the post linked above to find instructions and source code for how to do this. You could also run a Minecraft server locally. The important thing is that you configure the provider with the address where your server is running, including the rcon port, and the rcon password for the server. Again, check my other blog post for details on this. You can also check out this great article by Mark Tinderholt.

I will use a local value map to define what type of block to place based on the character used in the input drawing:

locals {
  materials = {
    " " = "minecraft:air"
    "r" = "minecraft:red_wool"
    "o" = "minecraft:orange_wool"
    "y" = "minecraft:yellow_wool"
    "g" = "minecraft:lime_wool"
    "l" = "minecraft:light_blue_wool"
    "b" = "minecraft:blue_wool"
    "p" = "minecraft:purple_wool"
  }
}

I settled on using these eight types of blocks. The air block type is important since this signifies “nothing”. Perhaps we could skip these air blocks to increase the performance a bit, but if you build the structure where there are already other blocks you will want to have the air blocks as well.

I will use a variable for the starting coordinate (the origin) representing the lower left corner of the structure in the (x,y,z) space:

variable "origin" {
  type = string

  validation {
    condition     = length(split(",", var.origin)) == 3
    error_message = "Specify three coordinates x,y,z"
  }
}

The coordinate should be supplied as e.g. 1,2,3. Remember that the structure I create will be placed in the (x,y) plane, i.e. with a fixed z-coordinate.

I let the origin be the lower left corner because it is easier to walk around and find a good lower left corner coordinate in the Minecraft world, but I need to convert this coordinate to the upper left corner coordinate because the input image is defined starting in this corner. I do this by adjusting the y coordinate up by the height of the structure, here represented by the number of lines in the input data:

locals {
  # split the input origin coordinate into pieces
  # also convert to numbers, but we will need to redo this later
  start = [for c in split(",", var.origin) : tonumber(c)]

  # read the input data for the structure
  data  = file("${path.module}/input.txt")

  # split the data into lines
  lines = split("\n", local.data)

  # recalculate the new origin (upper left corner)
  origin = [
    local.start[0],
    local.start[1] + length(local.lines),
    local.start[2],
  ]
}

What is left to do is to go through each line and each character in the line and compute its coordinate. The following calculation produces one list of coordinates on the form <material>,x,y,z:

locals {
  coordinates = compact(flatten([
    for i in range(length(local.lines)) : [
      for j in range(length(local.lines[i])) : "${substr(local.lines[i], j, 1)},${local.origin[0] + j},${local.origin[1] - i},${local.origin[2]}"
    ]
  ]))
}

The x-coordinate increases with the j loop index, and the y-coordinate decreases with the i loop index (because we start at the upper left corner and work our way down to the lower right corner).

To produce the blocks in the Minecraft world I use the minecraft_block resource type with a for_each going through all the coordinates:

resource "minecraft_block" "all" {
  for_each = toset(local.coordinates)

  material = local.materials[split(",", each.value)[0]]

  position = {
    x = tonumber(split(",", each.value)[1])
    y = tonumber(split(",", each.value)[2])
    z = tonumber(split(",", each.value)[3])
  }
}

Testing it out
#

To test the solution I have created the following larger Terraform logo using different colors of blocks:

bbb
bbbbbb
bbbbbbbbb
bbbbbbbbbbbb
pbbbbbbbbbbbbbl
ppbbbbbbbbbbbbblll
pppbbbbbbbbbbbbblllll
ppppbbbbbbbbbbbbbllllllll
pppppbbbbbbbbbbbbbllllllll
ppppppbbbbbbbbbbbbblllllll  lll                                                   rr
pppppppbbbbbbbbbbbbbllllll  lllllg                                             rrrrr
ppppppppbbbbbbbbbbbbblllll  llllllggg                                      rrrrrrrrr
pppppppppbbbbbbbbbbbbbllll  lllllllggggg                                oorrrrrrrrrr
ppppppppppbbbbbbbbbbbbblll  llllllllggggggg                          oooooorrrrrrrrr
pppppppppppbbbbbbbbbbbbbll  lllllllllgggggggggg                   oooooooooorrrrrrrr
ppppppppppppbbbbbbbbbbbbbl  llllllllllgggggggggggg             yooooooooooooorrrrrrr
pppppppppppppbbbbbbbbbbbbb  lllllllllllgggggggggggggy       yyyyyooooooooooooorrrrrr
   pppppppppppbbbbbbbbbbbb  llllllllllllgggggggggggggyy   yyyyyyyyooooooooooooorrrrr
      pppppppppbbbbbbbbbbb  lllllllllllllgggggggggggggy   yyyyyyyyyooooooooooooorrrr
         pppppppbbbbbbbbbb  blllllllllllllggggggggggggg   yyyyyyyyyyooooooooooooorrr
            pppppbbbbbbbbb  bblllllllllllllgggggggggggg   yyyyyyyyyyyooooooooooooorr
               pppbbbbbbbb  bbblllllllllllllggggggggggg   yyyyyyyyyyyyooooooooooooor
                  pbbbbbbb  bbbblllllllllllllgggggggggg   yyyyyyyyyyyyyooooooooooooo
                     bbbbb  bbbbblllllllllllllggggggggg   gyyyyyyyyyyyyyoooooooooooo
                         b  bbbbbblllllllllllllgggggggg   ggyyyyyyyyyyyyyooooooooooo
                            bbbbbbblllllllllllllggggggg   gggyyyyyyyyyyyyyoooooooooo
                               bbbbblllllllllllllgggggg   ggggyyyyyyyyyyyyyooooooo
                            bb    bbblllllllllllllggggg   gggggyyyyyyyyyyyyyooo
                            bbbbb    blllllllllllllgggg   ggggggyyyyyyyyyyy
                            bbbbbbbb    llllllllllllggg   gggggggyyyyyyy
                            bbbbbbbbbbb    llllllllllgg   ggggggggyyy
                            bbbbbbbbbbbbbll    lllllllg   gggggggg
                            pbbbbbbbbbbbbbllll    lllll   ggggg
                            ppbbbbbbbbbbbbbllllll    ll   gg
                            pppbbbbbbbbbbbbbllllllll
                            ppppbbbbbbbbbbbbbllllllllll
                            pppppbbbbbbbbbbbbblllllllll
                            ppppppbbbbbbbbbbbbbllllllll
                            pppppppbbbbbbbbbbbbblllllll
                            ppppppppbbbbbbbbbbbbbllllll
                            pppppppppbbbbbbbbbbbbblllll
                            ppppppppppbbbbbbbbbbbbbllll
                            pppppppppppbbbbbbbbbbbbblll
                            ppppppppppppbbbbbbbbbbbbbll
                              pppppppppppbbbbbbbbbbbbbl
                                 pppppppppbbbbbbbbbbbbb
                                    pppppppbbbbbbbbbbbb
                                       pppppbbbbbbbbbbb
                                           ppbbbbbbbbbb
                                              bbbbbbbbb
                                                 bbbbbb
                                                    bbb

Placing this content in input.txt, configuring input values to all my variables, and running terraform apply produces the following structure in my Minecraft world:

A Terraform logo in rainbow colors in the Minecraft world

This was pretty successful!

Conclusions and future work
#

This post has shown a simple way to produce 2D structures in the Minecraft world using Terraform.

I believe you can extend this idea to 3D structures, but you need to come up with a convenient way of defining such structures in a text file. If you are reading this and feel up for the challenge: let me know what you come up with!

I’ll throw one idea out right now: you could define your 3D structures layer by layer in the (x,z)-plane. This could become quite complex and produce large input files. However, I do not think it is possible to get around this fact. With that said, maybe there is a way to mathematically describe the structure you want to create using a simple formula? This would be very concise, but would of course put severe limits on what you can create.

Mattias Fjellström
Author
Mattias Fjellström
Cloud architect · Author · HashiCorp Ambassador · Microsoft MVP