In [ ]:
#!/usr/bin/env python


# mcpipy.com retrieved from URL below, written by SleepyOz
# http://www.raspberrypi.org/phpBB3/viewtopic.php?f=32&t=33427

import mcpi.block as block
import mcpi.minecraft as minecraft
import time


"""
Dot matrix digits 5 wide by 8 high.
0 - voxel should be drawn
Anything else - voxel should be cleared
"""
digit_dots = {
    '0':[
        ' 000',
        '0   0',
        '0   0',
        '0   0',
        '0   0',
        '0   0',
        '0   0',
        ' 000',
        ],
    '1':[
        '  0',
        ' 00',
        '  0',
        '  0',
        '  0',
        '  0',
        '  0',
        ' 000',
        ],
    '2':[
        ' 000',
        '0   0',
        '   0',
        '  0',
        ' 0',
        '0',
        '0',
        '00000',
        ],
    '3':[
        ' 000',
        '0   0',
        '    0',
        '  00',
        '    0',
        '    0',
        '0   0',
        ' 000',
        ],
    '4':[
        '   0',
        '  00',
        ' 0 0',
        '0  0',
        '00000',
        '   0',
        '   0',
        '   0',
        ],
    '5':[
        '00000',
        '0',
        '0',
        '0000',
        '    0',
        '    0',
        '0   0',
        ' 000',
        ],
    '6':[
        ' 000',
        '0   0',
        '0',
        '0000',
        '0   0',
        '0   0',
        '0   0',
        ' 000',
        ],
    '7':[
        '00000',
        '    0',
        '   0',
        '  0',
        ' 0',
        '0',
        '0',
        '0',
        ],
    '8':[
        ' 000',
        '0   0',
        '0   0',
        ' 000',
        '0   0',
        '0   0',
        '0   0',
        ' 000',
        ],
    '9':[
        ' 000',
        '0   0',
        '0   0',
        ' 0000',
        '    0',
        '    0',
        '0   0',
        ' 000',
        ],
    ':':[
        '',
        '',
        '  00',
        '  00',
        '',
        '  00',
        '  00',
        '',
        ],
    }

class buffer:
    """
    Double-buffer a voxel message for Minecraft.
    To improve performance, only changes are rendered.
    """
    anchor_position = minecraft.Vec3(0,0,0)
    last_message = ''
    offscreen = []
    onscreen = []
    unset = block.OBSIDIAN
    set = block.SNOW_BLOCK

    def __init__(self, anchor_position):
        """
        Set everything up to render messages into the world
        at the given position.
        """
        self.anchor_position = anchor_position

    def draw_base(self, client):
        """
        Build some foundations for the clock.
        """
        # Foundations of stone.
        for y in range(-5, -1): # Nice thick base.
            for x in range(-1, 8*6): # 8 digits each 6 wide, plus a border, minus one to cut out the space beside the last digit.
                for z in range(-1, 8+1): # Each digit is 8 high, plus a border.
                    client.setBlock(self.anchor_position.x+x, self.anchor_position.y+y, self.anchor_position.z+z, block.STONE)
            # Shallow pool at the top.
        for x in range(0, 8*6-1):
            for z in range(0, 8):
                client.setBlock(self.anchor_position.x+x, self.anchor_position.y-2, self.anchor_position.z+z, block.WATER_STATIONARY)

    def render(self, message):
        """
        Put message into the off-screen buffer.
        """
        if message != self.last_message: # Do nothing if the message has not changed.
            self.last_message =  message # For next time.

            self.offscreen = [] # Clear any previous use of the buffer.
            letter_offset = 0
            for letter in message:
                rendition = digit_dots[letter]
                line_offset = 0
                for line in rendition:
                    if len(self.offscreen) <= line_offset:
                        # Make space to store the drawing.
                        self.offscreen.append([])
                    dot_offset = 0
                    for dot in line:
                        if dot == '0':
                            self.offscreen[line_offset].append(self.set)
                        else:
                            self.offscreen[line_offset].append(self.unset)
                        dot_offset += 1
                    for blank in range(dot_offset, 6):
                        # Expand short lines to the full width of 6 voxels.
                        self.offscreen[line_offset].append(self.unset)
                    line_offset += 1
                letter_offset += 1

            # Clear the onscreen buffer.
            # Should only happen on the first call.
            # Assumption: message will always be the same size.
            # Assumption: render() is called before flip().
            if self.onscreen == []:
                # No onscreen copy yet - so make it the same size as the offscreen image. Fill with suitable voxels.
                line_offset = 0
                for line in self.offscreen:
                    self.onscreen.append([])
                    for dot in line:
                        self.onscreen[line_offset].append(block.DIRT)
                    line_offset += 1

    def flip(self, client):
        """
        Put the off-screen buffer onto the screen.
        Only send the differences.
        Remember the new screen for next flip.
        Draw the clock inverted so it read properly from above.
        """
        line_offset = 0
        height = len(self.offscreen) - 1
        for line in self.offscreen:
            dot_offset = 0
            length = len(line) - 2 # Fit into the border better.
            for dot in line:
                if self.onscreen[line_offset][dot_offset] != dot:
                    self.onscreen[line_offset][dot_offset] = dot
                    client.setBlock(self.anchor_position.x+length-dot_offset, self.anchor_position.y-3, self.anchor_position.z+height-line_offset, dot)
                dot_offset += 1
            line_offset += 1

client=minecraft.Minecraft.create() # Connect to Minecraft.
place=client.player.getPos() # Start near the player.
# place.y is just below the player, and we don't need to change it.
bitmapper = buffer(place)

bitmapper.draw_base(client)
while True:
    timestr = time.strftime("%H:%M:%S") # Format time nicely.
    bitmapper.render(timestr)
    bitmapper.flip(client)
    time.sleep(.1) # Rest a while before drawing again.