Re-organize grid management code for less dependance between files

Remove gridio.lua and move its function to appropriate files
This commit is contained in:
Gaël C 2024-02-01 19:30:07 +01:00
parent 2cf3b19167
commit 146f009684
4 changed files with 98 additions and 127 deletions

View File

@ -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

View File

@ -1,6 +1,6 @@
-- Manages grid loading, writing and generation -- 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 = {} local registered_on_grid_loaded = {}
function mapgen_rivers.register_on_grid_loaded(func) function mapgen_rivers.register_on_grid_loaded(func)
@ -21,31 +21,64 @@ local function offset_conv(o)
return (o + 0.5) * (1/256) return (o + 0.5) * (1/256)
end end
local grid_maps_list = { local floor = math.floor
dem = {bytes=2, signed=true}, local sbyte, schar = string.byte, string.char
lakes = {bytes=2, signed=true}, local unpk = unpack
dirs = {bytes=1, signed=false},
rivers = {bytes=4, signed=false},
offset_x = {bytes=1, signed=true, conv=offset_conv}, -- Loading files
offset_y = {bytes=1, signed=true, conv=offset_conv},
-- 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 -- First, check whether a grid is already loaded
if mapgen_rivers.grid then if mapgen_rivers.grid then
return true 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 end
-- Fall back to loading the grid from the files -- If not, try to load the grid from the files
local sfile = io.open(world_data .. 'size', 'r') local sfile = io.open(datapath .. 'size', 'r')
if not sfile then 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 end
local x, z = sfile:read('*n'), sfile:read('*n') 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') minetest.log("action", '[mapgen_rivers] Loading grid')
grid = { local grid = {
size = {x=x, y=z}, 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 mapgen_rivers.grid = grid
on_grid_loaded_callback(grid) on_grid_loaded_callback(grid)
return true return true
end 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

View File

@ -8,17 +8,8 @@ mapgen_rivers.world_data_path = minetest.get_worldpath() .. '/river_data/'
dofile(modpath .. 'settings.lua') dofile(modpath .. 'settings.lua')
dofile(modpath .. 'gridmanager.lua') dofile(modpath .. 'gridmanager.lua')
dofile(modpath .. 'gridio.lua')
dofile(modpath .. 'polygons.lua') dofile(modpath .. 'polygons.lua')
dofile(modpath .. 'heightmap.lua') dofile(modpath .. 'heightmap.lua')
dofile(modpath .. 'mapgen.lua') dofile(modpath .. 'mapgen.lua')
minetest.register_on_mods_loaded(function() minetest.register_on_mods_loaded(mapgen_rivers.load_or_generate_grid)
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)

View File

@ -1,6 +1,8 @@
-- Generate the grid using terrainlib_lua -- Generate the grid using terrainlib_lua
-- Only called on first mapgen, if there is no grid yet -- Only called on first mapgen, if there is no grid yet
-- Constants
local EvolutionModel = dofile(mapgen_rivers.modpath .. '/terrainlib_lua/erosion.lua') local EvolutionModel = dofile(mapgen_rivers.modpath .. '/terrainlib_lua/erosion.lua')
local twist = dofile(mapgen_rivers.modpath .. '/terrainlib_lua/twist.lua') local twist = dofile(mapgen_rivers.modpath .. '/terrainlib_lua/twist.lua')
@ -52,12 +54,10 @@ local function margin(dem, width, elev)
end end
end end
-- Generate the grid -- Generate grid
minetest.log("action", '[mapgen_rivers] Generating grid, this may take a while...') minetest.log("action", '[mapgen_rivers] Generating grid, this may take a while...')
local grid = {size={x=X, y=Y}}
if X*Y > 4e6 then 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.") 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 end
@ -103,19 +103,48 @@ model:flow()
local mfloor = math.floor local mfloor = math.floor
local mmin, mmax = math.min, math.max local mmin, mmax = math.min, math.max
local unpk, schar = unpack, string.char
local offset_x, offset_y = twist(model.dirs, model.rivers, 5) local offset_x, offset_y = twist(model.dirs, model.rivers, 5)
for i=1, X*Y do for i=1, X*Y do
offset_x[i] = mmin(mmax(offset_x[i]*256, -128), 127) offset_x[i] = mmin(mmax(offset_x[i]*256, -128), 127)
offset_y[i] = mmin(mmax(offset_y[i]*256, -128), 127) offset_y[i] = mmin(mmax(offset_y[i]*256, -128), 127)
end end
grid.dem = model.dem -- Write data
grid.lakes = model.lakes
grid.dirs = model.dirs
grid.rivers = model.rivers
grid.offset_x = offset_x
grid.offset_y = offset_y
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)