From 6c1d30a13075152d1e0b5afd35b71fef8fd43bb0 Mon Sep 17 00:00:00 2001 From: kay27 Date: Wed, 5 Jan 2022 06:43:16 +0400 Subject: [PATCH] Polish new mapgen stuff --- mods/CORE/mcl_mapgen/API.md | 88 +++++++----- mods/CORE/mcl_mapgen/init.lua | 133 +++++++++++++----- mods/CORE/mcl_mapgen/mod.conf | 2 +- .../api/mob_functions/environment.lua | 16 +-- mods/MAPGEN/mcl_biomes/init.lua | 3 +- mods/MAPGEN/mcl_mapgen_core/init.lua | 24 +--- 6 files changed, 164 insertions(+), 102 deletions(-) diff --git a/mods/CORE/mcl_mapgen/API.md b/mods/CORE/mcl_mapgen/API.md index 20829e183..75307f321 100644 --- a/mods/CORE/mcl_mapgen/API.md +++ b/mods/CORE/mcl_mapgen/API.md @@ -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: diff --git a/mods/CORE/mcl_mapgen/init.lua b/mods/CORE/mcl_mapgen/init.lua index 7e49f5d11..d2224075b 100644 --- a/mods/CORE/mcl_mapgen/init.lua +++ b/mods/CORE/mcl_mapgen/init.lua @@ -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,33 +86,31 @@ 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 + 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} table.sort(queue_blocks_lvm, function(a, b) return (a.order <= b.order) end) @@ -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 @@ -126,15 +138,19 @@ minetest.register_on_generated(function(minp, maxp, chunkseed) data = vm:get_data(lvm_buffer) area = VoxelArea:new({MinEdge=emin, MaxEdge=emax}) vm_context = { - data = data, - area = area, + data = data, + data2 = data2, + light = light, + area = area, + lvm_buffer = lvm_buffer, lvm_param2_buffer = lvm_param2_buffer, - vm = vm, - emin = emin, - emax = emax, - minp = minp, - maxp = maxp, - chunkseed = chunkseed + lvm_light_buffer = lvm_light_buffer, + vm = vm, + emin = emin, + emax = emax, + minp = minp, + maxp = maxp, + 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,9 +272,14 @@ minetest.register_on_generated(function(minp, maxp, chunkseed) if vm_context.write_param2 then vm:set_param2_data(data2) end - vm:calc_lighting(minp, maxp, vm_context.shadow or true) -- TODO: check boundaries - vm:write_to_map() - vm:update_liquids() + 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 @@ -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 diff --git a/mods/CORE/mcl_mapgen/mod.conf b/mods/CORE/mcl_mapgen/mod.conf index 4f896b3cf..76b4a5c93 100644 --- a/mods/CORE/mcl_mapgen/mod.conf +++ b/mods/CORE/mcl_mapgen/mod.conf @@ -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 diff --git a/mods/ENTITIES/mcl_mobs/api/mob_functions/environment.lua b/mods/ENTITIES/mcl_mobs/api/mob_functions/environment.lua index 5c431135e..847a4e0c7 100644 --- a/mods/ENTITIES/mcl_mobs/api/mob_functions/environment.lua +++ b/mods/ENTITIES/mcl_mobs/api/mob_functions/environment.lua @@ -175,16 +175,16 @@ end -- check if within physical map limits (-30911 to 30927) -- 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 + local wmin, wmax + 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 - 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 -- get node but use fallback for nil or unknown diff --git a/mods/MAPGEN/mcl_biomes/init.lua b/mods/MAPGEN/mcl_biomes/init.lua index d9bb1fff1..56a3cfe11 100644 --- a/mods/MAPGEN/mcl_biomes/init.lua +++ b/mods/MAPGEN/mcl_biomes/init.lua @@ -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) diff --git a/mods/MAPGEN/mcl_mapgen_core/init.lua b/mods/MAPGEN/mcl_mapgen_core/init.lua index 129642470..b1e9d8e09 100644 --- a/mods/MAPGEN/mcl_mapgen_core/init.lua +++ b/mods/MAPGEN/mcl_mapgen_core/init.lua @@ -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)