Press "." to enter slideshow, once entered "PgUp" and "PgDn" to navigate, "Esc" to leave slideshow again.

The Children Yearn for the Mines

Importing Ultima IV maps into Minecraft MinetestLuanti

Nick Moore

This is a presentation about copying the Ultima IV maps into Minecraft, well not minecraft but a thing which was called Minetest and is now called Luanti. more stuff here
Procrastination
it's okay I'll fix that later
actually it's a talk about procrastination. Yesterday Ada gave a lightning talk about procrastination and I wanted to show that while students can procrastinate hard, senior devs can procrastinate harder.

The Primordial Code

TODO:

anyway I was working on yesterday's presentation about bioinformatics and I'd gotten as far as a to-do list which had lots to do and it was a lot of hard work but all I had to do was get on with it but ...
anyway so when I was a teenager I really loved this game called Ultima IV which ran on the apple 2 and even though there you saw the world through this tiny window ...
... there was a whole world there in your imagination. Places in games can seem really really even though they're just imaginary ...

Minecraft

Images of Minecraft are the intellectual property of Microsoft.
This is not a place of honor.
Do not fold, spindle or mutilate.
NO PICTURES. Anyway, when Minecraft came out I thought about how cool it would be if I could take the blocky maps from Ultima IV and put them into the blocky world of Minecraft. Several years later ...

Reading the Map

with open("dat/WORLD.MAP", "rb") as fh:
    ultima_world = fh.read(256*256)

def get_block(x, y):
    return ultima_world[(y//32)*8192+(x//32)*1024+(y%32)*32+(x%32)]
reading the Ultima IV map is really easy because computers were too primitive to be difficult and so it's just a bunch of bytes in a file but not in order of course because computers

Writing the Map

Minecraft
Minetest
Luanti
Instead of targeting Minecraft which is closed source and in Java `:-(` I decided to target Luanti which is open source and in Lua `:-/`
def block_to_data(block):
    # block is a binary array of node IDs in ZYX order.
    assert len(block) == 4096

    # headers

    yield 14 # flags: generated, lighting expired, day_night_differs, NOT is_underground
    yield from u16(0) # lighting needs recomputing in all directions
    yield from u32(0xffffffff) # timestamp

    # block mapping: only bother including blocks present in
    # this sector.

    present_blocks = set(block)
    present_block_map = [ (k, v) for k, v in block_map.items() if v in present_blocks ]

    yield 0    # mapping version
    yield from u16(len(present_block_map)) # length of block map
    for k, v in present_block_map:
        yield from u16(v)
        yield from u16(len(k))
	yield from bytes(k, 'ascii')
the file format for this is documented. It's very weird but at least it is documented. Well, more like "confessed".
I promised I'd show some python source code at this python conference so here it is. There's no syntax highlighting. Git gud.
it builds a bunch of binary data ...
yield 2 # content_width (always 2)
    yield 2 # params_width  (always 2)

    for b in block:
        yield from u16(b) # param0

    for b in block:
        yield 0           # param1

    for b in block:
        yield 0           # param2

    yield from u32(0)

    yield 10             # timer record size (always 10)
    yield from u16(0)  # no timers
... and some metadata which I don't really understand but I just set it mostly to 0 and it sort of works.
def block_to_binary(block):
    yield 29              # chunk version number
    yield from zstd.compress(bytes(block_to_data(block)))

db = sqlite3.connect('/home/nick/.minetest/worlds/x/map.sqlite')

def write_block(x, y, z, block):
    pos = z * 4096 * 4096 + y * 4096 + x
    data = bytes(block_to_binary(block))
    db.execute("insert or replace into blocks (pos, data) values (?, ?)", (pos, data))
Then it writes it into sqlite as a zlib compressed blob.
Confession.
Now I could write 16x16x16 blocks of random crap into Luanti.
this is a bit of the Ultima IV map
here it is copied into Luanti, scaled up 1:12.
there's also towns and castles and stuff
... which have their own 32x32 maps so I just write them into the map at the appropriate places, scaled 1:1.
that worked.
it all looked a bit square and ugly though
so I implemented a filter to round everything off
and that looked a lot better

Ultima IV in Luanti

so now I had a whole list of other things to do: mountains needed to grow up and oceans grow down and it needed trees and dungeons and shrines and moongates and it was all a bit hard and tedious.
Procrastination
so i procrastinated
https://nick.zoic.org/PYCON25/
and that's how I finished my bioinformatics presentation slides