diff --git a/gridio.lua b/gridio.lua deleted file mode 100644 index 424917b..0000000 --- a/gridio.lua +++ /dev/null @@ -1,69 +0,0 @@ --- Input and output functions for grid maps - -local worldpath = mapgen_rivers.world_data_path - -local floor = math.floor -local sbyte, schar = string.byte, string.char -local unpk = unpack - --- Loading files - --- Never load the full map during mapgen. Instead, create an empty lookup table --- and read the file on-the-fly when an element is requested for the first time, --- using __index metamethod. -local loader_mt = { - __index = function(loader, i) -- Called when accessing a missing key - local file = loader.file - local bytes = loader.bytes - file:seek('set', (i-1)*bytes) - local strnum = file:read(bytes) - - local n = sbyte(strnum, 1) - if loader.signed and n >= 128 then - n = n - 256 - end - - for j=2, bytes do - n = n*256 + sbyte(strnum, j) - end - - if loader.conv then - n = loader.conv(n) - end - -- Cache key for next use - loader[i] = n - return n - end, -} - -function mapgen_rivers.load_file(filename, bytes, signed, size, converter) - local file = io.open(worldpath .. filename, 'rb') - if file then - converter = converter or false - return setmetatable({file=file, bytes=bytes, signed=signed, size=size, conv=converter}, loader_mt) - end -end - --- Writing files -function mapgen_rivers.write_file(filename, data, bytes) - local file = io.open(worldpath .. filename, 'wb') - - local bytelist = {} - for j=1, bytes do - bytelist[j] = 0 - end - - for i=1, #data do - local n = floor(data[i]) - data[i] = n - for j=bytes, 2, -1 do - bytelist[j] = n % 256 - n = floor(n / 256) - end - bytelist[1] = n % 256 - - file:write(schar(unpk(bytelist))) - end - - file:close() -end diff --git a/gridmanager.lua b/gridmanager.lua index ff56292..312245f 100644 --- a/gridmanager.lua +++ b/gridmanager.lua @@ -1,6 +1,6 @@ -- Manages grid loading, writing and generation -local world_data = mapgen_rivers.world_data_path +local datapath = mapgen_rivers.world_data_path local registered_on_grid_loaded = {} function mapgen_rivers.register_on_grid_loaded(func) @@ -21,31 +21,64 @@ local function offset_conv(o) return (o + 0.5) * (1/256) end -local grid_maps_list = { - dem = {bytes=2, signed=true}, - lakes = {bytes=2, signed=true}, - dirs = {bytes=1, signed=false}, - rivers = {bytes=4, signed=false}, +local floor = math.floor +local sbyte, schar = string.byte, string.char +local unpk = unpack - offset_x = {bytes=1, signed=true, conv=offset_conv}, - offset_y = {bytes=1, signed=true, conv=offset_conv}, +-- Loading files + +-- Never load the full map during mapgen. Instead, create an empty lookup table +-- and read the file on-the-fly when an element is requested for the first time, +-- using __index metamethod. +local loader_mt = { + __index = function(loader, i) -- Called when accessing a missing key + local file = loader.file + local bytes = loader.bytes + file:seek('set', (i-1)*bytes) + local strnum = file:read(bytes) + + local n = sbyte(strnum, 1) + if loader.signed and n >= 128 then + n = n - 256 + end + + for j=2, bytes do + n = n*256 + sbyte(strnum, j) + end + + if loader.conv then + n = loader.conv(n) + end + -- Cache key for next use + loader[i] = n + return n + end, } -function mapgen_rivers.try_load_grid(grid) +local function load_file(filename, bytes, signed, size, converter) + local file = io.open(datapath .. filename, 'rb') + if file then + converter = converter or false + return setmetatable({file=file, bytes=bytes, signed=signed, size=size, conv=converter}, loader_mt) + end +end + +function mapgen_rivers.load_or_generate_grid() -- First, check whether a grid is already loaded if mapgen_rivers.grid then return true - -- Then, check the provided argument is a valid grid - elseif grid then - mapgen_rivers.grid = grid - on_grid_loaded_callback(grid) - return true end - -- Fall back to loading the grid from the files - local sfile = io.open(world_data .. 'size', 'r') + -- If not, try to load the grid from the files + local sfile = io.open(datapath .. 'size', 'r') if not sfile then - return false + dofile(mapgen_rivers.modpath .. "/pregenerate.lua") + collectgarbage() + + sfile = io.open(datapath .. 'size', 'r') + if not sfile then + return false + end end local x, z = sfile:read('*n'), sfile:read('*n') @@ -55,31 +88,18 @@ function mapgen_rivers.try_load_grid(grid) minetest.log("action", '[mapgen_rivers] Loading grid') - grid = { + local grid = { size = {x=x, y=z}, + dem = load_file('dem', 2, true, x*z), + lakes = load_file('lakes', 2, true, x*z), + dirs = load_file('dirs', 1, false, x*z), + rivers = load_file('rivers', 4, false, x*z), + offset_x = load_file('offset_x', 1, true, x*z, offset_conv), + offset_y = load_file('offset_y', 1, true, x*z, offset_conv), } - for map, params in pairs(grid_maps_list) do - grid[map] = mapgen_rivers.load_file(map, params.bytes, params.signed, x*z, params.conv) - end - mapgen_rivers.grid = grid on_grid_loaded_callback(grid) return true end - -function mapgen_rivers.write_grid(grid) - minetest.mkdir(world_data) - - for map, params in pairs(grid_maps_list) do - mapgen_rivers.write_file(map, grid[map], params.bytes) - end - - local sfile = io.open(world_data .. 'size', "w") - sfile:write(grid.size.x..'\n'..grid.size.y) - sfile:close() - - return true -end - diff --git a/init.lua b/init.lua index fcec84c..c8c2886 100644 --- a/init.lua +++ b/init.lua @@ -8,17 +8,8 @@ mapgen_rivers.world_data_path = minetest.get_worldpath() .. '/river_data/' dofile(modpath .. 'settings.lua') dofile(modpath .. 'gridmanager.lua') -dofile(modpath .. 'gridio.lua') dofile(modpath .. 'polygons.lua') dofile(modpath .. 'heightmap.lua') dofile(modpath .. 'mapgen.lua') -minetest.register_on_mods_loaded(function() - local exist = mapgen_rivers.try_load_grid() - - if not exist then -- If grid does not exist yet, generate it - local grid = dofile(modpath .. 'pregenerate.lua') - mapgen_rivers.write_grid(grid) - mapgen_rivers.try_load_grid() -- Reload if needed - end -end) +minetest.register_on_mods_loaded(mapgen_rivers.load_or_generate_grid) diff --git a/pregenerate.lua b/pregenerate.lua index 80d7191..97341ad 100644 --- a/pregenerate.lua +++ b/pregenerate.lua @@ -1,6 +1,8 @@ -- Generate the grid using terrainlib_lua -- Only called on first mapgen, if there is no grid yet +-- Constants + local EvolutionModel = dofile(mapgen_rivers.modpath .. '/terrainlib_lua/erosion.lua') local twist = dofile(mapgen_rivers.modpath .. '/terrainlib_lua/twist.lua') @@ -52,12 +54,10 @@ local function margin(dem, width, elev) end end --- Generate the grid +-- Generate grid minetest.log("action", '[mapgen_rivers] Generating grid, this may take a while...') -local grid = {size={x=X, y=Y}} - if X*Y > 4e6 then minetest.log("warning", "[mapgen_rivers] You are going to generate a very large grid (>4M nodes). If you experience problems, you should increase blocksize or reduce map size.") end @@ -103,19 +103,48 @@ model:flow() local mfloor = math.floor local mmin, mmax = math.min, math.max +local unpk, schar = unpack, string.char local offset_x, offset_y = twist(model.dirs, model.rivers, 5) for i=1, X*Y do offset_x[i] = mmin(mmax(offset_x[i]*256, -128), 127) offset_y[i] = mmin(mmax(offset_y[i]*256, -128), 127) end -grid.dem = model.dem -grid.lakes = model.lakes -grid.dirs = model.dirs -grid.rivers = model.rivers -grid.offset_x = offset_x -grid.offset_y = offset_y +-- Write data -collectgarbage() +local datapath = mapgen_rivers.world_data_path +minetest.mkdir(datapath) -return grid +local sfile = io.open(datapath .. 'size', "w") +sfile:write(X..'\n'..Y) +sfile:close() + +local function write_file(filename, data, bytes) + local file = io.open(datapath .. filename, 'wb') + + local bytelist = {} + for j=1, bytes do + bytelist[j] = 0 + end + + for i=1, #data do + local n = mfloor(data[i]) + data[i] = n + for j=bytes, 2, -1 do + bytelist[j] = n % 256 + n = mfloor(n / 256) + end + bytelist[1] = n % 256 + + file:write(schar(unpk(bytelist))) + end + + file:close() +end + +write_file('dem', model.dem, 2) +write_file('lakes', model.lakes, 2) +write_file('dirs', model.dirs, 1) +write_file('rivers', model.rivers, 4) +write_file('offset_x', offset_x, 1) +write_file('offset_y', offset_y, 1)