2024-01-02 17:59:51 +01:00
local wea_c = worldeditadditions_core
local wea = worldeditadditions
local Vector3 = wea_c.Vector3
---
-- @module worldeditadditions
2020-10-10 22:43:22 +02:00
-- ███████ ██ ██ ██████ ██████ ██ ██ ██ ██ ██████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ██ ██ ██████ ██ ██ ██ ██ ██ ██ ██ ██ █████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ██████ ██████ ██████ ██ ████ ██ ██████ ███████
2024-01-02 17:59:51 +01:00
-- NOTE this isn't a trip dash --- 'cause it's local-only to this file
2020-10-10 22:43:22 +02:00
-- Counts the number of chunks in the given area.
2021-02-03 03:25:33 +01:00
-- Maths is now done properly. Values from this new implementation were tested
-- with 1000 random pos1, pos2, and chunk_size combinations and found to be identical.
2024-01-02 17:59:51 +01:00
-- @internal
-- @param pos1 Vector3 Position 1 of the defined region.
-- @param pos2 Vector3 Position 2 of the defined region.
-- @param chunk_size Vector3 Size of the chunks to subdivide into..
2020-10-10 22:43:22 +02:00
local function count_chunks ( pos1 , pos2 , chunk_size )
2021-02-03 03:25:33 +01:00
-- Assume pos1 & pos2 are sorted
2023-07-15 22:59:24 +02:00
local dimensions = Vector3.new (
pos2.x - pos1.x + 1 ,
pos2.y - pos1.y + 1 ,
pos2.z - pos1.z + 1
)
2021-02-03 03:25:33 +01:00
-- print("[new] dimensions", dimensions.x, dimensions.y, dimensions.z)
return math.ceil ( dimensions.x / chunk_size.x )
* math.ceil ( dimensions.y / chunk_size.y )
* math.ceil ( dimensions.z / chunk_size.z )
2020-10-10 22:43:22 +02:00
end
2021-02-03 03:25:33 +01:00
2020-10-10 22:43:22 +02:00
local function merge_stats ( a , b )
for key , value in pairs ( a ) do
if not b [ key ] then b [ key ] = 0 end
b [ key ] = a [ key ] + b [ key ]
end
end
local function make_stats_obj ( state )
return {
chunks_total = state.chunks_total ,
chunks_completed = state.chunks_completed ,
chunk_size = state.chunk_size ,
2020-10-10 22:57:19 +02:00
volume_nodes = state.volume_nodes ,
2020-10-10 22:43:22 +02:00
emerge = state.stats_emerge ,
times = state.times ,
eta = state.eta ,
emerge_overhead = state.emerge_overhead
}
end
local function subdivide_step_complete ( state )
2022-09-18 23:58:33 +02:00
state.times . total = wea_c.get_ms_time ( ) - state.times . start
2020-10-10 22:43:22 +02:00
state.callback_complete (
state.pos1 ,
state.pos2 ,
make_stats_obj ( state )
)
end
local function subdivide_step_beforeload ( state )
2020-10-10 22:50:03 +02:00
state.cpos . z = state.cpos . z - ( state.chunk_size . z + 1 )
2020-10-10 23:22:53 +02:00
if state.cpos . z < state.pos1 . z then
2020-10-10 22:57:19 +02:00
state.cpos . z = state.pos2 . z
2020-10-10 22:50:03 +02:00
state.cpos . y = state.cpos . y - ( state.chunk_size . y + 1 )
2020-10-10 23:22:53 +02:00
if state.cpos . y < state.pos1 . y then
2020-10-10 22:43:22 +02:00
state.cpos . y = state.pos2 . y
2020-10-10 22:50:03 +02:00
state.cpos . x = state.cpos . x - ( state.chunk_size . x + 1 )
2020-10-10 23:22:53 +02:00
if state.cpos . x < state.pos1 . x then
2020-10-10 22:43:22 +02:00
subdivide_step_complete ( state )
return
end
end
end
2023-07-15 22:59:24 +02:00
state.cpos2 = Vector3.new ( state.cpos . x , state.cpos . y , state.cpos . z )
state.cpos1 = Vector3.new (
state.cpos . x - state.chunk_size . x ,
state.cpos . y - state.chunk_size . y ,
state.cpos . z - state.chunk_size . z
)
2020-10-10 22:43:22 +02:00
if state.cpos1 . x < state.pos1 . x then state.cpos1 . x = state.pos1 . x end
if state.cpos1 . y < state.pos1 . y then state.cpos1 . y = state.pos1 . y end
if state.cpos1 . z < state.pos1 . z then state.cpos1 . z = state.pos1 . z end
2022-09-18 23:58:33 +02:00
state.times . emerge_last = wea_c.get_ms_time ( )
2021-02-07 02:02:03 +01:00
2022-09-18 23:58:33 +02:00
-- print("[BEFORE_EMERGE] c1", state.cpos1, "c2", state.cpos2, "volume", worldedit.volume(state.cpos1, state.cpos2))
2022-09-19 20:42:22 +02:00
wea_c.emerge_area ( state.cpos1 , state.cpos2 , state.__afterload , state )
2020-10-10 22:43:22 +02:00
end
2020-10-10 22:57:19 +02:00
2020-10-10 22:43:22 +02:00
local function subdivide_step_afterload ( state_emerge , state_ours )
2022-09-18 23:58:33 +02:00
-- print("[AFTER_EMERGE] c1", state_ours.cpos1, "c2", state_ours.cpos2, "volume", worldedit.volume(state_ours.cpos1, state_ours.cpos2))
state_ours.times . emerge_last = wea_c.get_ms_time ( ) - state_ours.times . emerge_last
2020-10-10 22:43:22 +02:00
table.insert ( state_ours.times . emerge , state_ours.times . emerge_last )
2021-02-03 04:04:33 +01:00
if # state_ours.times . emerge > 25 then
2022-09-18 23:58:33 +02:00
state_ours.times . emerge = wea_c.table . get_last ( state_ours.times . emerge , 100 )
2021-02-03 04:04:33 +01:00
end
state_ours.times . emerge_total = state_ours.times . emerge_total + state_ours.times . emerge_last
2020-10-10 22:43:22 +02:00
merge_stats ( state_emerge.stats , state_ours.stats_emerge )
2021-02-07 04:12:09 +01:00
state_ours.chunks_completed = state_ours.chunks_completed + 1
2022-09-18 23:58:33 +02:00
local callback_last = wea_c.get_ms_time ( )
2020-10-10 22:43:22 +02:00
state_ours.callback_subblock (
state_ours.cpos1 ,
state_ours.cpos2 ,
make_stats_obj ( state_ours )
)
2022-09-18 23:58:33 +02:00
state_ours.times . callback_last = wea_c.get_ms_time ( ) - callback_last
2020-10-10 22:43:22 +02:00
table.insert ( state_ours.times . callback , state_ours.times . callback_last )
2022-09-18 23:58:33 +02:00
state_ours.times . step_last = wea_c.get_ms_time ( ) - state_ours.times . step_start_abs
2020-10-10 22:43:22 +02:00
table.insert ( state_ours.times . steps , state_ours.times . step_last )
2021-02-03 04:04:33 +01:00
if # state_ours.times . steps > 25 then
2022-09-18 23:58:33 +02:00
state_ours.times . steps = wea_c.table . get_last ( state_ours.times . steps , 100 )
2021-02-03 04:04:33 +01:00
end
state_ours.times . steps_total = state_ours.times . steps_total + state_ours.times . step_last
2022-09-18 23:58:33 +02:00
state_ours.times . step_start_abs = wea_c.get_ms_time ( )
state_ours.eta = wea_c.eta (
2021-02-07 03:45:34 +01:00
state_ours.times . steps ,
state_ours.chunks_completed ,
state_ours.chunks_total
)
2020-10-10 22:43:22 +02:00
if state_ours.chunks_completed > 0 then
2021-02-03 04:04:33 +01:00
state_ours.emerge_overhead = state_ours.times . emerge_total / state_ours.times . steps_total
2020-10-10 22:43:22 +02:00
end
2020-10-10 22:57:19 +02:00
minetest.after ( 0 , state_ours.__beforeload , state_ours )
2020-10-10 22:43:22 +02:00
end
--- Calls the given callback function once for each block in the defined area.
-- This function is asynchronous, as it also uses minetest.emerge_area() to
-- ensure that the blocks are loaded before calling the callback function.
-- The callback functions will be passed the following arguments: pos1, pos2, stats
-- pos1 and pos2 refer to the defined region of just the local block.
-- stats is an table of statistics resembling the following:
-- { chunks_completed, chunks_total, emerge = { ... }, times = { emerge = {}, emerge_last, callback = {}, callback_last, steps = {}, step_last } }
-- The emerge property contains a table that holds a running total of statistics
-- about what Minetest did to emerge the requested blocks in the world.
-- callback_complete is called at the end of the process, and pos1 + pos2 will be set to that of the entire region.
2024-01-02 17:59:51 +01:00
-- @param pos1 Vector The first position defining the area to emerge.
-- @param pos2 Vector The second position defining the area to emerge.
-- @param chunk_size Vector The size of the chunks to subdivide into.
-- @param callback function The callback to call for each block.
-- @param callback function The callback to call upon completion.
2020-10-10 22:43:22 +02:00
function worldeditadditions . subdivide ( pos1 , pos2 , chunk_size , callback_subblock , callback_complete )
pos1 , pos2 = worldedit.sort_pos ( pos1 , pos2 )
2020-10-10 23:22:53 +02:00
local chunks_total = count_chunks ( pos1 , pos2 , chunk_size )
2020-10-10 22:43:22 +02:00
chunk_size.x = chunk_size.x - 1 -- WorldEdit regions are inclusive
chunk_size.y = chunk_size.y - 1 -- WorldEdit regions are inclusive
chunk_size.z = chunk_size.z - 1 -- WorldEdit regions are inclusive
local state = {
pos1 = pos1 , pos2 = pos2 ,
2020-10-10 23:22:53 +02:00
-- Note that we start 1 over on the Z axis because we increment *before* calling the callback, so if we don't fiddle it here, we'll miss the first chunk
2023-07-15 22:59:24 +02:00
cpos = Vector3.new ( pos2.x , pos2.y , pos2.z + chunk_size.z + 1 ) ,
2020-10-10 22:43:22 +02:00
-- The size of a single subblock
chunk_size = chunk_size ,
-- The total number of nodes in the defined region
volume_nodes = worldedit.volume ( pos1 , pos2 ) ,
stats_emerge = { } ,
times = {
-- Total time per step
2021-02-03 04:04:33 +01:00
steps_total = 0 ,
2022-09-18 23:58:33 +02:00
steps = { } , step_last = 0 , step_start_abs = wea_c.get_ms_time ( ) ,
2020-10-10 22:43:22 +02:00
-- Time per step spent on mineteest.emerge_area()
2021-02-03 04:04:33 +01:00
emerge_total = 0 ,
2020-10-10 22:43:22 +02:00
emerge = { } , emerge_last = 0 ,
-- Timme per step spent running the callback
callback = { } , callback_last = 0 ,
-- The start time (absolute)
2022-09-18 23:58:33 +02:00
start = wea_c.get_ms_time ( ) ,
2020-10-10 22:43:22 +02:00
-- The eta (in ms) until we're done
eta = 0
} ,
-- The percentage of the total time spent so far waiting for Minetest to emerge blocks. Updated every step.
emerge_overhead = 0 ,
-- The total number of chunks
2020-10-10 23:22:53 +02:00
chunks_total = chunks_total ,
2020-10-10 22:43:22 +02:00
-- The number of chunks processed so far
chunks_completed = 0 ,
callback_subblock = callback_subblock ,
2020-10-10 22:57:19 +02:00
callback_complete = callback_complete ,
__beforeload = subdivide_step_beforeload ,
__afterload = subdivide_step_afterload
2020-10-10 22:43:22 +02:00
}
subdivide_step_beforeload ( state )
end