Skip to content
Go to file
Cannot retrieve contributors at this time
356 lines (236 sloc) 6.93 KB

What I won’t be talking about




Why Minecraft?

  • Minecraft being used as a teaching aid
  • Provides motivating objectives for youngsters
  • 3D world gives immediate feedback

Already bindings to Minecraft for Ruby and Python, but I wanted to be able to explore in Clojure.


Many thanks to Martin O’Hanlon (

Several Python libraries, including Minecraft Turtle


What I’ll be showing you

  • Redstone for interacting with Minecraft from Clojure
  • PPM image file format
  • tools.cli for parsing command line arguments

Does Minecraft have a public API?

  • The official game doesn’t
  • The Raspberry Pi version does

The API is very limited

No Pi? No Problem!

  • CraftBukkit is a version of the game server designed for plugin development
  • RaspberryJuice plugin replicates API


A library for interacting with Minecraft: Pi Edition

  • Query the block at a coordinate
  • Query the player’s position
  • Set blocks at coordinates
  • Set the player’s position
  • Set a callback on block hit
  • Send a message to the game chat

Example: Query a block

(require '[redstone.client :as mc])

(def server
  {:host "localhost"
   :port 4711})

;; Query player's tile position (which block are they on?)
(let [player-position (mc/player-tile-position server)
      ;; Position is {:x x, :y y, :z z}
      position-under-player (update-in player-position [:y] dec)]
  (-> server
      ;; What sort of block is it?
      (mc/block-at position-under-player))

Example: Query a block


Blocks are maps

Minecraft is a world of blocks. There are hundreds of different blocks made out of different materials.

In redstone these are represented as a map.

(mc/block-at position-under-player)

;; Might return something like:
=> {:id 4 :data 0}



Example: Setting

;; Positions are maps too
(def position {:x 25 :y 55 :z 22})

(mc/set-block-at! server position {:id 4 :data 0})

;; equivalent to
(mc/set-block-at! server position :cobblestone)

;; Block names are kebab-case...
(mc/set-block-at! server position :red-flower)

;; ...with optional data values
(mc/set-block-at! server position :red-flower)   ;; Poppy
(mc/set-block-at! server position :red-flower:4) ;; Tulip

Coloured Wool


(mc/set-block-at! server position :wool:0) ;; White
(mc/set-block-at! server position :wool:1) ;; Orange
(mc/set-block-at! server position :wool:2) ;; Magenta
;; ...etc

;; Convenience names
(mc/set-block-at! server position :red-wool)


(defprotocol RPCArgument
  (as-rpc-arg [_]))

(extend-protocol RPCArgument
  (as-rpc-arg [kw]
    (when-let [{:keys [id data]} (get name->block kw)]
      [id data]))

  (as-rpc-arg [x] x)

  (as-rpc-arg [s] s)

  (as-rpc-arg [xs]
    (remove nil? ((juxt :x :y :z :id :data) xs)))

  (as-rpc-arg [xs] (flatten (map as-rpc-arg xs)))

  (as-rpc-arg [tf] (if tf 1 0)))

I/O, I/O, it’s off to work we go

Format minecraft consumes:


Redstone API:

(mc/set-block-at! server {:x 1 :y 2 :z 3} :red-wool)

;; Resolves to:

Query / Command

Redstone API is just named query and command fns

(def block-at
  "The block at the specified position"
  (query "world.getBlockWithData"
         #(->> (s/split % #",")
               (map parse-long)
               (zipmap [:id :data]))))

(def set-block-at!
  "Sets the block at the given coordinates"
  (command "world.setBlock"))

Striking gold

;; Define a handler
(defn midas-touch [server event]
  (mc/set-block-at! server (:position event) :gold-block))

;; Register the handler
(mc/listen! server :block:hit midas-touch)

Minecraft doesn’t support push events, so this is done with polling inside a future.

What’s it good for?

Originally started working on this around Easter



Project Icon

An image for the Redstone README


Colour dithering, wanted a generic image import

PPM image format

It’s grossly inefficient, but very easy to read.

4 4

…a black 4x4 pixel image.


Generating PPMs is easy with ImageMagick

$> convert input.png -resize 10 output.ppm


  • File contains arbitrary data
  • Using nio’s mmap rather than slurp
(require '[nio.core :refer [mmap buffer-seq]])

;; Returns a sequence of unsigned byte values (0-255) as longs
(->> (mmap "/file/path.ppm")
     (map #(bit-and % 0xff)))


Stolen from the Clojure docs for min-key

(defn distance-squared [c1 c2]
  (->> (map - c1 c2)
       (map #(* % %))
       (reduce +)))

(def block-colours 
  {[221 221 221] :wool
   [219 126 63] :orange-wool
;; ...
   [150 53 49] :red-wool
   [26 23 23] :black-wool})

(defn rgb->block [colour-map rgb-triple]
   (apply min-key (partial distance-squared rgb-triple) (keys colour-map))))

What happens if…?


What happens if…?


Bruce Egg


The Wizard of Oz


Image Sequences

We can use ffmpeg to convert a movie into a sequence of jpegs.

$> ffmpeg -i -y output-dir/%09d.jpg

Then step through each frame and render it.


Parses arguments passed to a `-main` function.

(require '[ :refer [parse-opts]])

(def cli-options
  [["-f" "--file FILE" "File path"]
   ["-w" "--width BLOCKS" "Width of the screen in blocks"
    :parse-fn #(Long/parseLong %)
    :default 25]
   ["-c" "--clear" "Whether space should be cleared for the screen"]])

(defn -main [& args]
  (let [options (parse-opts args cli-options)]
    (when (:clear options)
      (clear-space! options))
    (draw-movie! options)))


Instructions for Minecraft server installation linked from README

Thank You



CTO, Likely

You can’t perform that action at this time.