Polish new mapgen stuff

This commit is contained in:
kay27 2022-01-05 06:43:16 +04:00
parent 4ce3102ab4
commit 6c1d30a130
6 changed files with 164 additions and 102 deletions

View File

@ -3,77 +3,95 @@
Helps to avoid problems caused by 'chunk-in-shell' feature of mapgen.cpp.
It also queues your generators to run them in proper order:
### mcl_mapgen.register_on_generated(callback_function, order_number)
For Minetest 5.4 it doesn't recommended to place blocks within callback function.
### mcl_mapgen.register_on_generated(lvm_callback_function, order_number)
=========================================================================
Replacement of engine API function `minetest.register_on_generated(function(minp, maxp, blockseed))`
It is still unsafe. Cavegen part can and will overwrite outer 1-block layer of the chunk which is expected to be generated.
Nodes marked as `is_ground_content` could be overwritten. Air and water are usually 'ground content' too.
For Minetest 5.4 it doesn't recommended to place blocks within lvm callback function.
See https://git.minetest.land/MineClone2/MineClone2/issues/1395
`callback_function`: chunk callback LVM function definition:
`lvm_callback_function`: chunk callback LVM function definition:
`function(vm_context)`:
Function MUST RETURN `vm_context` back anyway! It will passed into next callback function from the queue.
`vm_context`: a table which already contains some LVM data if the fields, and some of them can be added by you right in the callback function:
Function MUST RETURN `vm_context` back anyway! It will passed into next lvm callback function from the queue.
`vm_context`: a table which already contains some LVM data as the fields, and some of them can be added in your lvm callback function:
`vm`: curent voxel manipulator object itself;
`blockseed`: seed of this mapchunk;
`minp` & `maxp`: minimum and maximum chunk position;
`emin` & `emax`: minimum and maximum chunk position WITH SHELL AROUND IT;
`area`: voxel area, can be helpful to access data;
`data`: LVM buffer data array, data loads into it before the callbacks;
`write`: set it to true in yout callback functionm, if you changed `data` and want to write it;
`data2`: LVM buffer data array of `param2`, !NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - you load it yourfels:
`write`: set it to true in your lvm callback functionm, if you changed `data` and want to write it;
`data2`: LVM buffer data array of `param2`, !NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - you load it yourself:
`vm_context.data2 = vm_context.data2 or vm_context.vm.get_param2_data(vm_context.lvm_param2_buffer)`
`write_param2`: set it to true in yout callback functionm, if you used `data2` and want to write it;
`write_param2`: set it to true in your lvm callback function, if you used `data2` and want to write it;
`light`: LVM buffer data array of light, !NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - you load it yourself:
`vm_context.light = vm_context.light or vm_context.vm.get_light2_data(vm_context.lvm_light_buffer)`
`write_light`: set it to true in your lvm callback function, if you used `light` and want to write it;
`lvm_param2_buffer`: static `param2` buffer pointer, used to load `data2` array;
`shadow`: set it to false to disable shadow propagation;
`heightmap`: mapgen object contanting y coordinates of ground level,
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourfels:
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourself:
`vm_context.heightmap = vm_context.heightmap or minetest.get_mapgen_object('heightmap')`
`biomemap`: mapgen object contanting biome IDs of nodes,
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourfels:
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourself:
`vm_context.biomemap = vm_context.biomemap or minetest.get_mapgen_object('biomemap')`
`heatmap`: mapgen object contanting temperature values of nodes,
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourfels:
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourself:
`vm_context.heatmap = vm_context.heatmap or minetest.get_mapgen_object('heatmap')`
`humiditymap`: mapgen object contanting humidity values of nodes,
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourfels:
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourself:
`vm_context.humiditymap = vm_context.humiditymap or minetest.get_mapgen_object('humiditymap')`
`gennotify`: mapgen object contanting mapping table of structures, see Minetest Lua API for explanation,
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourfels:
!NO ANY DATA LOADS INTO IT BEFORE THE CALLBACKS! - load it yourself:
`vm_context.gennotify = vm_context.gennotify or minetest.get_mapgen_object('gennotify')`
`order_number` (optional): the less, the earlier,
e.g. `mcl_mapgen.order.BUILDINGS` or `mcl_mapgen.order.LARGE_BUILDINGS`
### mcl_mapgen.register_mapgen(callback_function, order_number)
==============================================================================
Registers callback function to be called when current chunk generation is finished.
`callback_function`: callback function definition:
`function(minp, maxp, seed)`:
`minp` & `maxp`: minimum and maximum chunk position;
`seed`: seed of this mapchunk;
### mcl_mapgen.register_mapgen_block_lvm(lvm_callback_function, order_number)
=============================================================================
Registers lvm callback function to be called when current block (usually 16x16x16 nodes) generation is REALLY 100% finished.
`vm_context` passes into lvm callback function and should always be returned back.
`lvm_callback_function`: the block callback LVM function definition - same as for chunks - see definition example above;
`order_number` (optional): the less, the earlier,
e.g. `mcl_mapgen.order.BUILDINGS` or `mcl_mapgen.order.LARGE_BUILDINGS`
### mcl_mapgen.register_mapgen_block(callback_function, order_number)
=======================================================================
Registers callback function to be called when block (usually 16x16x16 nodes) generation is finished.
`callback_function`: callback function definition:
### mcl_mapgen.register_mapgen_block(node_callback_function, order_number)
==========================================================================
Registers node_callback function to be called when current block (usually 16x16x16 nodes) generation is REALLY 100% finished.
`node_callback_function`: node callback function definition:
`function(minp, maxp, seed)`:
`minp` & `maxp`: minimum and maximum block position;
`seed`: seed of this mapblock;
`order_number` (optional): the less, the earlier,
e.g. `mcl_mapgen.order.BUILDINGS` or `mcl_mapgen.order.LARGE_BUILDINGS`
### mcl_mapgen.register_mapgen_block_lvm(callback_function, order_number)
============================================================================
Registers callback function to be called when block (usually 16x16x16 nodes) generation is finished.
`vm_context` passes into callback function and should be returned back.
`callback_function`: block callback LVM function definition, see below;
`order_number` (optional): the less, the earlier,
e.g. `mcl_mapgen.order.BUILDINGS` or `mcl_mapgen.order.LARGE_BUILDINGS`
### mcl_mapgen.register_mapgen(callback_function, order_number)
====================================================================
Registers callback function to be called when current chunk generation is REALLY 100% finished.
For LVM it's the most frustrating function from this mod.
It can't provide you access to mapgen objects. They are probably gone long ago.
Don't use it for accessing mapgen objects please.
To use VM you have to run `vm_context.vm = minetest.get_voxel_manip(vm_context.emin, vm_context.emax)`.
Set
`callback_function`: callback function definition:
`function(minp, maxp, seed, vm_context)`:
`minp` & `maxp`: minimum and maximum block position;
`seed`: seed of this mapblock;
`vm_context`: a table - see description above.
`order_number` (optional): the less, the earlier.
### mcl_mapgen.register_mapgen_lvm(callback_function, order_number)
============================================================================
### mcl_mapgen.register_mapgen_lvm(lvm_callback_function, order_number)
=======================================================================
Registers lvm callback function to be called when current chunk generation is REALLY 100% finished.
It's the most frustrating function from this mod. It can't provide you access to mapgen objects. They are probably gone long ago.
Don't use it for accessing mapgen objects please.
`vm_context` passes into lvm callback function and should always be returned back.
`lvm_callback_function`: the block callback LVM function definition - same as above;
`order_number` (optional): the less, the earlier.
### mcl_mapgen.get_far_node(pos)
===============================
Returns node if it is generated. Otherwise returns `{name = "ignore"}`.
================================
Returns node if it is generated, otherwise returns `{name = "ignore"}`.
## Constants:

View File

@ -44,7 +44,7 @@ mcl_mapgen.EDGE_MIN = central_chunk_min_pos - numcmin * mcl_mapgen.CS_NODES
mcl_mapgen.EDGE_MAX = central_chunk_max_pos + numcmax * mcl_mapgen.CS_NODES
minetest_log("action", "[mcl_mapgen] World edges: mcl_mapgen.EDGE_MIN = " .. tostring(mcl_mapgen.EDGE_MIN) .. ", mcl_mapgen.EDGE_MAX = " .. tostring(mcl_mapgen.EDGE_MAX))
------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Mapgen variables
local overworld, end_, nether = {}, {}, {}
@ -58,10 +58,24 @@ mcl_mapgen.normal = not mcl_mapgen.superflat and not mcl_mapgen.singlenode
local superflat, singlenode, normal = mcl_mapgen.superflat, mcl_mapgen.singlenode, mcl_mapgen.normal
minetest_log("action", "[mcl_mapgen] Mapgen mode: " .. (normal and "normal" or (superflat and "superflat" or "singlenode")))
------------------------------------------
----------------------------------------------------------------------------------------------------------------------------
-- Generator queues
local queue_unsafe_engine = {}
local queue_chunks_nodes = {}
local queue_chunks_lvm = {}
local queue_blocks_nodes = {}
local queue_blocks_lvm = {}
-- Requirements. 0 means 'none', greater than 0 means 'required'
local block = 0
local queue_blocks_lvm_counter = 0
local lvm_chunk = 0
local param2 = 0
local nodes_block = 0
local nodes_chunk = 0
local safe_functions = 0
local queue_unsafe, queue_blocks_lvm, queue_lvm, queue_blocks, queue = {}, {}, {}, {}, {} -- Generators' queues
local lvm, block, queue_blocks_lvm_counter, lvm_chunk, param2, nodes_block, nodes_chunk, safe_functions = 0, 0, 0, 0, 0, 0, 0, 0 -- Requirements: 0 means none; greater than 0 means 'required'
local BS, CS = mcl_mapgen.BS, mcl_mapgen.CS -- Mapblock size (in nodes), Mapchunk size (in blocks)
local LAST_BLOCK, LAST_NODE = CS - 1, BS - 1 -- First mapblock in chunk (node in mapblock) has number 0, last has THIS number. It's for runtime optimization
local offset = mcl_mapgen.OFFSET -- Central mapchunk offset (in blocks)
@ -72,32 +86,30 @@ local CS_3D = CS * CS * CS
local DEFAULT_ORDER = order.DEFAULT
function mcl_mapgen.register_on_generated(callback_function, order)
queue_unsafe[#queue_unsafe+1] = {i = priority or DEFAULT_ORDER, f = callback_function}
table.sort(queue_lvm, function(a, b) return (a.i <= b.i) end)
queue_unsafe_engine[#queue_unsafe_engine+1] = {i = priority or DEFAULT_ORDER, f = callback_function}
table.sort(queue_unsafe_engine, function(a, b) return (a.i <= b.i) end)
end
function mcl_mapgen.register_mapgen(callback_function, order)
nodes_chunk = nodes_chunk + 1
safe_functions = safe_functions + 1
queue[nodes_chunk] = {i = order or DEFAULT_ORDER, f = callback_function}
table.sort(queue, function(a, b) return (a.i <= b.i) end)
queue_chunks_nodes[nodes_chunk] = {i = order or DEFAULT_ORDER, f = callback_function}
table.sort(queue_chunks_nodes, function(a, b) return (a.i <= b.i) end)
end
function mcl_mapgen.register_mapgen_lvm(callback_function, order)
lvm = lvm + 1
lvm_chunk = lvm_chunk + 1
safe_functions = safe_functions + 1
queue_lvm[lvm_chunk] = {i = priority or DEFAULT_ORDER, f = callback_function}
table.sort(queue_lvm, function(a, b) return (a.i <= b.i) end)
queue_chunks_lvm[lvm_chunk] = {i = priority or DEFAULT_ORDER, f = callback_function}
table.sort(queue_chunks_lvm, function(a, b) return (a.i <= b.i) end)
end
function mcl_mapgen.register_mapgen_block(callback_function, priority)
block = block + 1
nodes_block = nodes_block + 1
safe_functions = safe_functions + 1
queue_blocks[nodes_block] = {i = priority or DEFAULT_ORDER, f = callback_function}
table.sort(queue_blocks, function(a, b) return (a.i <= b.i) end)
queue_blocks_nodes[nodes_block] = {i = priority or DEFAULT_ORDER, f = callback_function}
table.sort(queue_blocks_nodes, function(a, b) return (a.i <= b.i) end)
end
function mcl_mapgen.register_mapgen_block_lvm(callback_function, order)
block = block + 1
lvm = lvm + 1
queue_blocks_lvm_counter = queue_blocks_lvm_counter + 1
safe_functions = safe_functions + 1
queue_blocks_lvm[queue_blocks_lvm_counter] = {order = order or DEFAULT_ORDER, callback_function = callback_function}
@ -113,10 +125,10 @@ minetest.register_on_shutdown(function()
end)
local vm_context -- here will be many references and flags, like: param2, light_data, heightmap, biomemap, heatmap, humiditymap, gennotify, write_lvm, write_param2, shadow
local data, data2, area
local data, data2, light, area
local current_blocks = {}
local current_chunks = {}
local lvm_buffer, lvm_param2_buffer = {}, {} -- Static buffer pointers
local lvm_buffer, lvm_param2_buffer, lvm_light_buffer = {}, {}, {} -- Static buffer pointers
minetest.register_on_generated(function(minp, maxp, chunkseed)
local minp, maxp, chunkseed = minp, maxp, chunkseed
@ -127,14 +139,18 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
area = VoxelArea:new({MinEdge=emin, MaxEdge=emax})
vm_context = {
data = data,
data2 = data2,
light = light,
area = area,
lvm_buffer = lvm_buffer,
lvm_param2_buffer = lvm_param2_buffer,
lvm_light_buffer = lvm_light_buffer,
vm = vm,
emin = emin,
emax = emax,
minp = minp,
maxp = maxp,
chunkseed = chunkseed
chunkseed = chunkseed,
}
if safe_functions > 0 then
@ -163,7 +179,7 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
box = block_pos_offset_removed % CS
if not blocks[bx] then blocks[bx]={} end
-- We don't know how many calls, including this one, will overwrite this block's content!
-- We don't know how many calls, including this one, will overwrite this block content!
-- Start calculating it with `total_mapgen_block_writes_through_x` variable.
-- It can be `8 or less`, if we (speaking of `x` axis) are on chunk edge now,
-- or it can be `4 or less` - if we are in the middle of the chunk by `x` axis:
@ -197,7 +213,7 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
local total_mapgen_block_writes = (boz > 0 and boz < LAST_BLOCK) and math_floor(total_mapgen_block_writes_through_y / 2) or total_mapgen_block_writes_through_y
-- Get current number of writes from the table, or just set it to 1, if accessed first time:
-- Get current number of writes from the table, or just set it to 1, if accessing first time:
local current_mapgen_block_writes = blocks[bx][by][bz] and (blocks[bx][by][bz] + 1) or 1
@ -246,8 +262,8 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
end
end
if lvm > 0 then
for _, v in pairs(queue_lvm) do
if #queue_unsafe_engine > 0 then
for _, v in pairs(queue_unsafe_engine) do
vm_context = v.f(vm_context)
end
if vm_context.write then
@ -256,10 +272,15 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
if vm_context.write_param2 then
vm:set_param2_data(data2)
end
if vm_context.write_light then
vm:set_light_data(light)
end
if vm_context.write or vm_context.write_param2 or vm_context.write_light then
vm:calc_lighting(minp, maxp, vm_context.shadow or true) -- TODO: check boundaries
vm:write_to_map()
vm:update_liquids()
end
end
for i, b in pairs(current_chunks) do
local cx, cy, cz, seed = b.x, b.y, b.z, b.s
@ -267,14 +288,46 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
local x, y, z = bx * BS, by * BS, bz * BS
local minp = {x = x, y = y, z = z}
local maxp = {x = x + CS_NODES - 1, y = y + CS_NODES - 1, z = z + CS_NODES - 1}
for _, v in pairs(queue) do
v.f(minp, maxp, seed)
area = VoxelArea:new({MinEdge=minp, MaxEdge=maxp})
vm_context = {
data = data,
data2 = data2,
light = light,
area = area,
lvm_buffer = lvm_buffer,
lvm_param2_buffer = lvm_param2_buffer,
lvm_light_buffer = lvm_light_buffer,
emin = minp,
emax = maxp,
minp = minp,
maxp = maxp,
chunkseed = seed,
}
for _, v in pairs(queue_chunks_lvm) do
v.f(vm_context)
end
for _, v in pairs(queue_chunks_nodes) do
v.f(minp, maxp, seed, vm_context)
end
if vm_context.write or vm_context.write_param2 or vm_context.write_light then
if vm_context.write then
vm:set_data(data)
end
if vm_context.write_param2 then
vm:set_param2_data(data2)
end
if vm_context.write_light then
vm:set_light_data(light)
end
vm:calc_lighting(minp, maxp, vm_context.shadow or true)
vm:write_to_map()
vm:update_liquids()
end
current_chunks[i] = nil
end
for i, b in pairs(current_blocks) do
for _, v in pairs(queue_blocks) do
for _, v in pairs(queue_blocks_nodes) do
v.f(b.minp, b.maxp, b.seed)
end
current_blocks[i] = nil
@ -399,3 +452,13 @@ mcl_mapgen.end_ = end_
mcl_mapgen.nether = nether
mcl_mapgen.order = order
function mcl_mapgen.get_voxel_manip(vm_context)
if vm_context.vm then
return vm
end
vm_context.vm = minetest.get_voxel_manip(vm_context.emin, vm_context.emax)
vm_context.emin, vm_context.emax = vm_context.vm:read_from_map(vm_context.emin, vm_context.emax)
vm_context.area = VoxelArea:new({MinEdge=vm_context.emin, MaxEdge=vm_context.emax})
return vm_context.vm
end

View File

@ -1,4 +1,4 @@
name = mcl_mapgen
author = kay27
description = MineClone 2 MapGen Basic Stuff
description = MineClone 2/5 MapGen Basic Stuff
depends = mcl_init

View File

@ -176,16 +176,16 @@ end
-- within_limits, wmin, wmax = nil, -30913, 30928
mobs.within_limits = function(pos, radius)
local wmin, wmax
if mcl_vars then
if mcl_vars.mapgen_edge_min and mcl_vars.mapgen_edge_max then
wmin, wmax = mcl_vars.mapgen_edge_min, mcl_vars.mapgen_edge_max
end
end
if mcl_mapgen then
if mcl_mapgen.EDGE_MIN and mcl_mapgen.EDGE_MAX then
wmin, wmax = mcl_mapgen.EDGE_MIN, mcl_mapgen.EDGE_MAX
return pos
and (pos.x - radius) > wmin and (pos.x + radius) < wmax
and (pos.y - radius) > wmin and (pos.y + radius) < wmax
and (pos.z - radius) > wmin and (pos.z + radius) < wmax
end
end
end
-- get node but use fallback for nil or unknown
mobs.node_ok = function(pos, fallback)

View File

@ -3995,7 +3995,8 @@ if not mcl_mapgen.singlenode then
local gennotify = vm_context.gennotify
for _, pos in pairs(gennotify["decoration#"..deco_id_chorus_plant] or {}) do
local realpos = { x = pos.x, y = pos.y + 1, z = pos.z }
minetest.after(1, mcl_end.grow_chorus_plant, realpos)
local pr = PseudoRandom(vm_context.blockseed)
minetest.after(1, mcl_end.grow_chorus_plant, realpos, false, pr)
end
return vm_context
end, mcl_mapgen.order.CHORUS)

View File

@ -988,28 +988,6 @@ local function register_mgv6_decorations()
end
-- Wet Sponge
-- TODO: Remove this when we got ocean monuments
minetest.register_decoration({
deco_type = "simple",
decoration = "mcl_sponges:sponge_wet",
spawn_by = {"group:water"},
num_spawn_by = 1,
place_on = {"mcl_core:dirt","mcl_core:sand"},
sidelen = 16,
noise_params = {
offset = 0.00295,
scale = 0.006,
spread = {x = 250, y = 250, z = 250},
seed = 999,
octaves = 3,
persist = 0.666
},
flags = "force_placement",
y_min = mcl_mapgen.overworld.lava_max + 5,
y_max = -20,
})
-- Add a small amount of tall grass everywhere to avoid areas completely empty devoid of tall grass
minetest.register_decoration({
deco_type = "simple",
@ -1197,6 +1175,8 @@ mcl_mapgen.register_mapgen_lvm(function(c)
if maxp.y < -5 or minp.y > 0 then
return c
end
c.vm = c.vm or mcl_mapgen.get_voxel_manip(c)
minetest.log("warning", "CLAY!")
local pr = PseudoRandom(blockseed)