PyuTest.register_world = function (options)
  local default_options = {}
  local conf = {}

  for k, v in pairs(options) do
    conf[k] = v
  end

  for k, v in pairs(default_options) do
    if conf[k] == nil then
      conf[k] = v
    end
  end

  if conf.y_max == nil or conf.y_min == nil then
    error("Please supply 'y_max' and 'y_min' to the options table!")
  end

  if conf.name == nil then
    error("Please supply 'name' in the options table!")
  end

  local World = {
    name = conf.name,
    y_max = conf.y_max,
    y_min = conf.y_min,
    biome_names = {}
  }

  function World:register_biome(o)
    local name = conf.name .. "-" .. o.name
    local cfg = PyuTest.util.tablecopy(o)
    cfg.node_stone = cfg.node_stone or conf.node_stone -- Defaults to nil
    cfg.node_water = cfg.node_water or "air"

    minetest.register_biome(PyuTest.util.tableconcat(cfg, {
      name = name,
      depth_top = 0,
      depth_filler = 0,
      y_max = conf.y_max,
      y_min = conf.y_min,
      node_river_water = cfg.node_water,
      node_cave_liquid = cfg.node_water,
    }))

    self.biome_names[#self.biome_names+1] = name
    return name
  end

  function World:register_ore(o)
    minetest.register_ore(PyuTest.util.tableconcat(o, {
      y_max = conf.y_max,
      y_min = conf.y_min,
    }))
  end

  function World:register_decoration(o)
    minetest.register_decoration(PyuTest.util.tableconcat(o, {
      y_max = conf.y_max,
      y_min = conf.y_min
    }))
  end

  function World:create_token(name, world, color, craftitem)
    local average = (conf.y_max + conf.y_min) / 2

    PyuTest.make_item(name, world .. " Token", {
      world_token = 1
    }, "pyutest-world-token.png", {
      color = color,
      stack_max = 16,
      on_use = function (itemstack, user, pointed_thing)
        local pos = user:get_pos()
        local npos = vector.new(pos.x, average, pos.y)
        minetest.do_item_eat(0, "", itemstack, user, pointed_thing)
        local range = 1

        user:set_pos(npos)
        minetest.sound_play({name = "spellbook_action", gain = 1}, {pos = npos})

        -- Spent hours trying to get this to work using mapblocks, just resorted to waiting a second.
        minetest.after(1.2, function ()
          PyuTest.dorange(npos, range, function (p)
            minetest.remove_node(p)
          end)

          for dx = -range, range do
            for dz = -range, range do
              minetest.set_node(npos + vector.new(dx, -2, dz), {
                name = "pyutest_blocks:obsidian_block"
              })

            end
          end

          minetest.set_node(npos + vector.new(0, -1, 0), {
            name = "pyutest_blocks:light"
          })

          user:set_pos(npos)
        end)
      end
    })

    minetest.register_craft({
      output = name,
      recipe = {
        craftitem,
        "pyutest_ores:emerald_shard"
      },
      type = "shapeless"
    })
  end

  return World
end

