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

	core.register_decoration({
		deco_type = "simple",
		decoration = "pyutest_blocks:bedrock",
		place_on = "group:block",
		sidelen = 80,
		fill_ratio = 10,
		y_min = conf.y_max,
		y_max = conf.y_max,
		flags = "force_placement"
	})

	core.register_decoration({
		deco_type = "simple",
		decoration = "pyutest_blocks:bedrock",
		place_on = "group:block",
		sidelen = 80,
		fill_ratio = 10,
		y_min = conf.y_min,
		y_max = conf.y_min,
		flags = "force_placement"
	})

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

	function World:register_biome(name, type, opts, overworld)
		local b = true
		if overworld then
			b = false
		end

		local newname = conf.name .. "-" .. name

		opts["_pyutest_sky_type"] = opts["_pyutest_sky_type"] or conf.sky_type or "plain"
		opts["_pyutest_sky_base_color"] = opts["_pyutest_sky_base_color"] or conf.sky_color or "#000000"

		PyuTest.register_overworld_biome(newname, type, PyuTest.util.tableconcat(opts, {
			depth_top = opts.depth_top or 0,
			depth_filler = opts.depth_filler or 0,
			y_max = opts.y_max or conf.y_max,
			y_min = opts.y_min or conf.y_min,
			node_water = opts.node_water or "air",
			node_river_water = opts.node_water or "air",
			node_cave_liquid = opts.node_water or "air",
		}), b)

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

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

	function World:register_decoration(o)
		core.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)
				local range = 1
				itemstack:take_item()

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

				local min = npos - vector.new(range, range, range)
				local max = npos + vector.new(range, range, range)

				local c_air = core.get_content_id("air")
				local c_obsidian = core.get_content_id("pyutest_blocks:obsidian_block")

				local vm = core.get_voxel_manip()
				local emin, emax = vm:read_from_map(min, max)
				local a = VoxelArea:new {
					MinEdge = emin,
					MaxEdge = emax
				}
				local data = vm:get_data()

				local center
				for x = emin.x, emax.x do
					for y = emin.y, emax.y do
						for z = emin.z, emax.z do
							local vi = a:index(x, y, z)

							if data[vi] ~= c_air then
								data[vi] = c_air
							end

							if y == emin.y then
								data[vi] = c_obsidian
							end
						end
					end
				end

				vm:set_data(data)
				vm:write_to_map(true)

				local ax = (emax.x + emin.x) / 2
				local az = (emax.z + emin.z) / 2
				local p = vector.new(ax, emin.y + 1, az)


				core.set_node(p, {
					name = "pyutest_blocks:light"
				})

				user:set_pos(p)

				return itemstack
			end
		})

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

	return World
end
