Server crash caused by faulty find_nodes_in_area() implementation #165

Closed
opened 2021-11-12 12:02:50 +01:00 by cora · 19 comments
Member
What happened?

Oysterity crashed a number of times, reportedly while a player was teleporting out of bounds (under the world).

This first one they claim they can trigger on purpose:

2021-11-12 01:58:48: ERROR[Main]: ServerError: AsyncErr: environment_Step: Runtime error from mod 'mcl_fire' in callback environment_Step(): ...s/oysterity/worldmods/monitoring/metrictypes/counter.lua:42: find_nodes_in_area(): area volume exceeds allowed value of 4096000
2021-11-12 01:58:48: ERROR[Main]: stack traceback:
2021-11-12 01:58:48: ERROR[Main]: 	[C]: in function 'f'
2021-11-12 01:58:48: ERROR[Main]: 	...s/oysterity/worldmods/monitoring/metrictypes/counter.lua:42: in function 'find_nodes_in_area'
2021-11-12 01:58:48: ERROR[Main]: 	...est/bin/../games/Mineclonia/mods/ITEMS/mcl_fire/init.lua:316: in function 'update_player_sound'
2021-11-12 01:58:48: ERROR[Main]: 	...est/bin/../games/Mineclonia/mods/ITEMS/mcl_fire/init.lua:389: in function 'globalstep'
2021-11-12 01:58:48: ERROR[Main]: 	...ds/oysterity/worldmods/monitoring/builtin/globalstep.lua:32: in function <...ds/oysterity/worldmods/monitoring/builtin/globalstep.lua:24>
2021-11-12 01:58:48: ERROR[Main]: 	/home/cora/minetest/bin/../builtin/game/register.lua:422: in function </home/cora/minetest/bin/../builtin/game/register.lua:406>
2021-11-12 01:58:48: ERROR[Main]: stack traceback:

This second one reportedly happened "not on purpose"

2021-11-12 01:57:27: ERROR[Main]: ServerError: AsyncErr: environment_Step: Runtime error from mod 'mcl_burning' in callback environment_Step(): ...s/oysterity/worldmods/monitoring/metrictypes/counter.lua:42: find_nodes_in_area(): area volume exceeds allowed value of 4096000
2021-11-12 01:57:27: ERROR[Main]: stack traceback:
2021-11-12 01:57:27: ERROR[Main]: 	[C]: in function 'f'
2021-11-12 01:57:27: ERROR[Main]: 	...s/oysterity/worldmods/monitoring/metrictypes/counter.lua:42: in function 'find_nodes_in_area'
2021-11-12 01:57:27: ERROR[Main]: 	...in/../games/Mineclonia/mods/ENTITIES/mcl_burning/api.lua:55: in function 'get_touching_nodes'
2021-11-12 01:57:27: ERROR[Main]: 	...in/../games/Mineclonia/mods/ENTITIES/mcl_burning/api.lua:221: in function 'catch_fire_tick'
2021-11-12 01:57:27: ERROR[Main]: 	...in/../games/Mineclonia/mods/ENTITIES/mcl_burning/api.lua:250: in function 'tick'
2021-11-12 01:57:27: ERROR[Main]: 	...n/../games/Mineclonia/mods/ENTITIES/mcl_burning/init.lua:27: in function 'globalstep'
2021-11-12 01:57:27: ERROR[Main]: 	...ds/oysterity/worldmods/monitoring/builtin/globalstep.lua:32: in function <...ds/oysterity/worldmods/monitoring/builtin/globalstep.lua:24>
2021-11-12 01:57:27: ERROR[Main]: 	/home/cora/minetest/bin/../builtin/game/register.lua:422: in function </home/cora/minetest/bin/../builtin/game/register.lua:406>
2021-11-12 01:57:27: ERROR[Main]: stack traceback:
What did I expect?

I expected the game not to crash

How to get it to happen

Probably by teleporting out of bounds (a number of void deaths by the person seems to corroborate that).

Environment

Mineclonia Version: dadf35200a (supposedly all mcl versions are vulnurable to this though)

Minetest Version: 5.4.1

##### What happened? Oysterity crashed a number of times, reportedly while a player was teleporting out of bounds (under the world). This first one they claim they can trigger on purpose: ``` 2021-11-12 01:58:48: ERROR[Main]: ServerError: AsyncErr: environment_Step: Runtime error from mod 'mcl_fire' in callback environment_Step(): ...s/oysterity/worldmods/monitoring/metrictypes/counter.lua:42: find_nodes_in_area(): area volume exceeds allowed value of 4096000 2021-11-12 01:58:48: ERROR[Main]: stack traceback: 2021-11-12 01:58:48: ERROR[Main]: [C]: in function 'f' 2021-11-12 01:58:48: ERROR[Main]: ...s/oysterity/worldmods/monitoring/metrictypes/counter.lua:42: in function 'find_nodes_in_area' 2021-11-12 01:58:48: ERROR[Main]: ...est/bin/../games/Mineclonia/mods/ITEMS/mcl_fire/init.lua:316: in function 'update_player_sound' 2021-11-12 01:58:48: ERROR[Main]: ...est/bin/../games/Mineclonia/mods/ITEMS/mcl_fire/init.lua:389: in function 'globalstep' 2021-11-12 01:58:48: ERROR[Main]: ...ds/oysterity/worldmods/monitoring/builtin/globalstep.lua:32: in function <...ds/oysterity/worldmods/monitoring/builtin/globalstep.lua:24> 2021-11-12 01:58:48: ERROR[Main]: /home/cora/minetest/bin/../builtin/game/register.lua:422: in function </home/cora/minetest/bin/../builtin/game/register.lua:406> 2021-11-12 01:58:48: ERROR[Main]: stack traceback: ``` This second one reportedly happened "not on purpose" ``` 2021-11-12 01:57:27: ERROR[Main]: ServerError: AsyncErr: environment_Step: Runtime error from mod 'mcl_burning' in callback environment_Step(): ...s/oysterity/worldmods/monitoring/metrictypes/counter.lua:42: find_nodes_in_area(): area volume exceeds allowed value of 4096000 2021-11-12 01:57:27: ERROR[Main]: stack traceback: 2021-11-12 01:57:27: ERROR[Main]: [C]: in function 'f' 2021-11-12 01:57:27: ERROR[Main]: ...s/oysterity/worldmods/monitoring/metrictypes/counter.lua:42: in function 'find_nodes_in_area' 2021-11-12 01:57:27: ERROR[Main]: ...in/../games/Mineclonia/mods/ENTITIES/mcl_burning/api.lua:55: in function 'get_touching_nodes' 2021-11-12 01:57:27: ERROR[Main]: ...in/../games/Mineclonia/mods/ENTITIES/mcl_burning/api.lua:221: in function 'catch_fire_tick' 2021-11-12 01:57:27: ERROR[Main]: ...in/../games/Mineclonia/mods/ENTITIES/mcl_burning/api.lua:250: in function 'tick' 2021-11-12 01:57:27: ERROR[Main]: ...n/../games/Mineclonia/mods/ENTITIES/mcl_burning/init.lua:27: in function 'globalstep' 2021-11-12 01:57:27: ERROR[Main]: ...ds/oysterity/worldmods/monitoring/builtin/globalstep.lua:32: in function <...ds/oysterity/worldmods/monitoring/builtin/globalstep.lua:24> 2021-11-12 01:57:27: ERROR[Main]: /home/cora/minetest/bin/../builtin/game/register.lua:422: in function </home/cora/minetest/bin/../builtin/game/register.lua:406> 2021-11-12 01:57:27: ERROR[Main]: stack traceback: ``` ##### What did I expect? I expected the game not to crash ##### How to get it to happen Probably by teleporting out of bounds (a number of void deaths by the person seems to corroborate that). ##### Environment Mineclonia Version: dadf35200a (supposedly all mcl versions are vulnurable to this though) Minetest Version: 5.4.1
cora added the
bug
unconfirmed
labels 2021-11-12 12:02:50 +01:00
Author
Member

this is the relevant code in mods/ITEMS/mcl_fire/init.lua:310...

function mcl_fire.update_player_sound(player)
    local player_name = player:get_player_name()
    -- Search for flame nodes in radius around player
    local ppos = player:get_pos()
    local areamin = vector.subtract(ppos, radius)
    local areamax = vector.add(ppos, radius)
    local fpos, num = minetest.find_nodes_in_area(
        areamin,
        areamax,
        {"mcl_fire:fire", "mcl_fire:eternal_fire"}
    )
    [...]

the distance between areamin and areamax is supposed to be constant (radius is defined as 8 in the file) so if this should become a problem I might just check for that.

this is the relevant code in mods/ITEMS/mcl_fire/init.lua:310... ``` function mcl_fire.update_player_sound(player) local player_name = player:get_player_name() -- Search for flame nodes in radius around player local ppos = player:get_pos() local areamin = vector.subtract(ppos, radius) local areamax = vector.add(ppos, radius) local fpos, num = minetest.find_nodes_in_area( areamin, areamax, {"mcl_fire:fire", "mcl_fire:eternal_fire"} ) [...] ``` the distance between areamin and areamax is supposed to be constant (radius is defined as 8 in the file) so if this should become a problem I might just check for that.
cora added the
critical
label 2021-11-12 12:06:05 +01:00
Owner

Turns out the volume calculation done by minetest.find_nodes_in_area() is actually incorrect.

Turns out the volume calculation done by `minetest.find_nodes_in_area()` is actually incorrect.
erlehmann changed title from Server crash caused by out of bounds players to Server crash caused by faulty find_nodes_in_area() implementation 2021-11-12 17:43:00 +01:00
Owner

luatic from #minetest IRC suggested that the volume calculation chokes on any coordinates exceeding s16 bounds. I can prove this with the following code, the new areatest command crashes Minetest:

minetest.register_chatcommand("areatest", {
func = function(name)
for x = 32500, 32767, 10 do
	for y = -32500, -32767, -10 do
		for z = 32500, 32767, 10 do
			local radius = 8
			local pos = { x=x, y=y, z=z }
			local areamin = vector.subtract(pos, radius)
			minetest.debug(
				"areamin",
				areamin.x,
				areamin.y,
				areamin.z
			)
			local areamax = vector.add(pos, radius)
			minetest.debug(
				"areamax",
				areamax.x,
				areamax.y,
				areamax.z
			)
			local npos, nnum = minetest.find_nodes_in_area(
			   areamin,
			   areamax,
			   "air"
			)
		end
	end
end
end
})
luatic from #minetest IRC suggested that the volume calculation chokes on any coordinates exceeding s16 bounds. I can prove this with the following code, the new `areatest` command crashes Minetest: ``` minetest.register_chatcommand("areatest", { func = function(name) for x = 32500, 32767, 10 do for y = -32500, -32767, -10 do for z = 32500, 32767, 10 do local radius = 8 local pos = { x=x, y=y, z=z } local areamin = vector.subtract(pos, radius) minetest.debug( "areamin", areamin.x, areamin.y, areamin.z ) local areamax = vector.add(pos, radius) minetest.debug( "areamax", areamax.x, areamax.y, areamax.z ) local npos, nnum = minetest.find_nodes_in_area( areamin, areamax, "air" ) end end end end }) ```
Author
Member

Sounds about right. I actually tested sth similar earlier. So o propose to add a is_in_bounds(pos) function in MCL_worlds and it before every find_nodes_in_area

Sounds about right. I actually tested sth similar earlier. So o propose to add a is_in_bounds(pos) function in MCL_worlds and it before every find_nodes_in_area
Author
Member

Its not like there s anything to be found outside of thay

Its not like there s anything to be found outside of thay
Owner

I have opened an issue on Minetest, titled “find_nodes_in_area() volume calculation is off by about four million nodes, enabling remotely-triggerable server crashes”:
https://github.com/minetest/minetest/issues/11769

I have opened an issue on Minetest, titled “find_nodes_in_area() volume calculation is off by about four million nodes, enabling remotely-triggerable server crashes”: https://github.com/minetest/minetest/issues/11769
Owner

add a is_in_bounds(pos) function in MCL_worlds and it before every find_nodes_in_area

How about we overwrite find_nodes_in_area with our own version that contains boundary checks though?

> add a is_in_bounds(pos) function in MCL_worlds and it before every find_nodes_in_area How about we overwrite find_nodes_in_area with our own version that contains boundary checks though?
Owner

How about we overwrite find_nodes_in_area with our own version that contains boundary checks though?

I'll test this.

> How about we overwrite find_nodes_in_area with our own version that contains boundary checks though? I'll test this.
Author
Member

Well maybe both. That check function might come in handy at other places

Well maybe both. That check function might come in handy at other places
Owner

New info from testing:

To crash the server, something needs to be bigger than the s16 bounds (-32768, 32767).

To not immediately crash it, but hang it at shutdown, you have to exactly hit the s16 bounds.

New info from testing: To crash the server, something needs to be bigger than the s16 bounds (-32768, 32767). To not immediately crash it, but hang it at shutdown, you have to *exactly* hit the s16 bounds.
Author
Member

New info from testing:

To crash the server, something needs to be bigger than the s16 bounds (-32768, 32767).

To not immediately crash it, but hang it at shutdown, you have to exactly hit the s16 bounds.

Well find_nodes makes no sense out of map bounds so we can safely just not do it I think

> New info from testing: > > To crash the server, something needs to be bigger than the s16 bounds (-32768, 32767). > > To not immediately crash it, but hang it at shutdown, you have to *exactly* hit the s16 bounds. Well find_nodes makes no sense out of map bounds so we can safely just not do it I think
Owner

I may have a fix. Note that I advocate clamping the coordinates for the calculation here only because it is arguably correct to say that there are no nodes there … except if you want to read server RAM from Lua, maybe?

function clamp(value, min, max)
	assert( min < max )
	if value < min then
		return min
	end
	if value > max then
		return max
	end
	return value
end

assert( clamp(000, -100, 100) == 000 )
assert( clamp(999, -100, 100) == 100 )
assert( clamp(999, -999, 999) == 999 )
assert( clamp(998, 999, 1999) == 999 )
assert( clamp(999, 999, 1999) == 999 )

function clamp_s16(value)
	-- seems minetest hangs on -32768 and 32767
	return clamp(value, -32767, 32766)
end

assert( clamp_s16(000000) == 000000 )
assert( clamp_s16(000001) == 000001 )
assert( clamp_s16(000010) == 000010 )
assert( clamp_s16(000100) == 000100 )
assert( clamp_s16(001000) == 001000 )
assert( clamp_s16(010000) == 010000 )
assert( clamp_s16(100000) == 032766 )

assert( clamp_s16(-00000) == -00000 )
assert( clamp_s16(-00009) == -00009 )
assert( clamp_s16(-00099) == -00099 )
assert( clamp_s16(-00999) == -00999 )
assert( clamp_s16(-09999) == -09999 )
assert( clamp_s16(-99999) == -32767 )

minetest_find_nodes_in_area = minetest.find_nodes_in_area
minetest.find_nodes_in_area = function(minp, maxp, nodenames)
	local result1, result2
	if
		minp.x >= 32767 or minp.x <= -32768 or
		minp.y >= 32767 or minp.y <= -32768 or
		minp.z >= 32767 or minp.z <= -32768 or
		maxp.x >= 32767 or maxp.x <= -32768 or
		maxp.y >= 32767 or maxp.y <= -32768 or
		maxp.z >= 32767 or maxp.z <= -32768
	then
		minetest.log(
			"warning",
			"find_nodes_in_area() called with coords outside interval (-32768, 32767), clamping arguments: " ..
			"minp { x=" .. minp.x .. ", y=" .. minp.y .. " z=" .. minp.z .. " } " ..
			"maxp { x=" .. maxp.x .. ", y=" .. maxp.y .. " z=" .. maxp.z .. " } "
		)
		result1, result2 = minetest_find_nodes_in_area(
			{ x=clamp_s16(minp.x), y=clamp_s16(minp.y), z=clamp_s16(minp.z) },
			{ x=clamp_s16(maxp.x), y=clamp_s16(maxp.y), z=clamp_s16(maxp.z) },
			nodenames
		)
	else
		result1, result2 = minetest_find_nodes_in_area(
			minp,
			maxp,
			nodenames
		)
	end
end

minetest.register_chatcommand("areatest", {
func = function(name)
for x = 32767, 32700, -10 do
	for y = -32768, -32700, 10 do
		for z = 32760, 32770, 0.1 do
			local radius = 8
			local pos = { x=x, y=y, z=z }
			local areamin = vector.subtract(pos, radius)
			local areamax = vector.add(pos, radius)
			local npos, nnum = minetest.find_nodes_in_area(
			   areamin,
			   areamax,
			   "air"
			)
		end
	end
end
end
})

I may have a fix. Note that I advocate clamping the coordinates for the calculation here *only* because it is arguably correct to say that there are no nodes there … except if you want to read server RAM from Lua, maybe? ``` function clamp(value, min, max) assert( min < max ) if value < min then return min end if value > max then return max end return value end assert( clamp(000, -100, 100) == 000 ) assert( clamp(999, -100, 100) == 100 ) assert( clamp(999, -999, 999) == 999 ) assert( clamp(998, 999, 1999) == 999 ) assert( clamp(999, 999, 1999) == 999 ) function clamp_s16(value) -- seems minetest hangs on -32768 and 32767 return clamp(value, -32767, 32766) end assert( clamp_s16(000000) == 000000 ) assert( clamp_s16(000001) == 000001 ) assert( clamp_s16(000010) == 000010 ) assert( clamp_s16(000100) == 000100 ) assert( clamp_s16(001000) == 001000 ) assert( clamp_s16(010000) == 010000 ) assert( clamp_s16(100000) == 032766 ) assert( clamp_s16(-00000) == -00000 ) assert( clamp_s16(-00009) == -00009 ) assert( clamp_s16(-00099) == -00099 ) assert( clamp_s16(-00999) == -00999 ) assert( clamp_s16(-09999) == -09999 ) assert( clamp_s16(-99999) == -32767 ) minetest_find_nodes_in_area = minetest.find_nodes_in_area minetest.find_nodes_in_area = function(minp, maxp, nodenames) local result1, result2 if minp.x >= 32767 or minp.x <= -32768 or minp.y >= 32767 or minp.y <= -32768 or minp.z >= 32767 or minp.z <= -32768 or maxp.x >= 32767 or maxp.x <= -32768 or maxp.y >= 32767 or maxp.y <= -32768 or maxp.z >= 32767 or maxp.z <= -32768 then minetest.log( "warning", "find_nodes_in_area() called with coords outside interval (-32768, 32767), clamping arguments: " .. "minp { x=" .. minp.x .. ", y=" .. minp.y .. " z=" .. minp.z .. " } " .. "maxp { x=" .. maxp.x .. ", y=" .. maxp.y .. " z=" .. maxp.z .. " } " ) result1, result2 = minetest_find_nodes_in_area( { x=clamp_s16(minp.x), y=clamp_s16(minp.y), z=clamp_s16(minp.z) }, { x=clamp_s16(maxp.x), y=clamp_s16(maxp.y), z=clamp_s16(maxp.z) }, nodenames ) else result1, result2 = minetest_find_nodes_in_area( minp, maxp, nodenames ) end end minetest.register_chatcommand("areatest", { func = function(name) for x = 32767, 32700, -10 do for y = -32768, -32700, 10 do for z = 32760, 32770, 0.1 do local radius = 8 local pos = { x=x, y=y, z=z } local areamin = vector.subtract(pos, radius) local areamax = vector.add(pos, radius) local npos, nnum = minetest.find_nodes_in_area( areamin, areamax, "air" ) end end end end }) ```
Author
Member

Uhh this seems unnecessarily complex (and technical) not sure I even understand it fully lol. I'd just check if the coords are within map bounds and else return an empty array tbh

Uhh this seems unnecessarily complex (and technical) not sure I even understand it fully lol. I'd just check if the coords are within map bounds and else return an empty array tbh
Author
Member

Ill have a counter proposal later which I almost finished this morning

Ill have a counter proposal later which I almost finished this morning
Owner

I'd just check if the coords are within map bounds and else return an empty array tbh

As I said in chat, that fails when the volume is partially out of bounds.

> I'd just check if the coords are within map bounds and else return an empty array tbh As I said in chat, that fails when the volume is partially out of bounds.
Author
Member

I found out exactly how to trigger it but I will not post publicly for crashbutton reasons.

EDIT: how to trigger both of them*

I found out exactly how to trigger it but I will not post publicly for [crashbutton](https://git.minetest.land/MineClone2/MineClone2/issues/1494) reasons. EDIT: how to trigger both of them*
Owner

@cora do these three work? If so, is there a difference in runtime?

function clamp(value, min, max)
	assert( min < max )
	if value < min then
		return min
	end
	if value > max then
		return max
	end
	return value
end

assert( clamp(000, -100, 100) == 000 )
assert( clamp(999, -100, 100) == 100 )
assert( clamp(999, -999, 999) == 999 )
assert( clamp(998, 999, 1999) == 999 )
assert( clamp(999, 999, 1999) == 999 )

function clamp_s16(value)
	-- seems minetest hangs on -32768 and 32767
	return clamp(value, -32767, 32766)
end

assert( clamp_s16(000000) == 000000 )
assert( clamp_s16(000001) == 000001 )
assert( clamp_s16(000010) == 000010 )
assert( clamp_s16(000100) == 000100 )
assert( clamp_s16(001000) == 001000 )
assert( clamp_s16(010000) == 010000 )
assert( clamp_s16(100000) == 032766 )

assert( clamp_s16(-00000) == -00000 )
assert( clamp_s16(-00009) == -00009 )
assert( clamp_s16(-00099) == -00099 )
assert( clamp_s16(-00999) == -00999 )
assert( clamp_s16(-09999) == -09999 )
assert( clamp_s16(-99999) == -32767 )

minetest_find_nodes_in_area = minetest.find_nodes_in_area
minetest.find_nodes_in_area1 = function(minp, maxp, ...)
	local result1, result2
	if
		minp.x >= 32767 or minp.x <= -32768 or
		minp.y >= 32767 or minp.y <= -32768 or
		minp.z >= 32767 or minp.z <= -32768 or
		maxp.x >= 32767 or maxp.x <= -32768 or
		maxp.y >= 32767 or maxp.y <= -32768 or
		maxp.z >= 32767 or maxp.z <= -32768
	then
		minetest.log(
			"warning",
			"find_nodes_in_area() called with coords outside interval (-32768, 32767), clamping arguments: " ..
			"minp { x=" .. minp.x .. ", y=" .. minp.y .. " z=" .. minp.z .. " } " ..
			"maxp { x=" .. maxp.x .. ", y=" .. maxp.y .. " z=" .. maxp.z .. " } "
		)
		result1, result2 = minetest_find_nodes_in_area(
			{ x=clamp_s16(minp.x), y=clamp_s16(minp.y), z=clamp_s16(minp.z) },
			{ x=clamp_s16(maxp.x), y=clamp_s16(maxp.y), z=clamp_s16(maxp.z) },
			...
		)
	else
		result1, result2 = minetest_find_nodes_in_area(
			minp,
			maxp,
			...
		)
	end
end

minetest.find_nodes_in_area2 = function(minp, maxp, ...)
	local result1, result2
	local clamped_minp = vector.apply(minp, clamp_s16)
	local clamped_maxp = vector.apply(maxp, clamp_s16)
	if not (vector.equals(clamped_minp, minp) and vector.equals(clamped_maxp, maxp)) then
		minetest.log(
			"warning",
			"find_nodes_in_area() called with coords outside interval (-32768, 32767), clamping arguments: " ..
			"minp { x=" .. minp.x .. ", y=" .. minp.y .. " z=" .. minp.z .. " } " ..
			"maxp { x=" .. maxp.x .. ", y=" .. maxp.y .. " z=" .. maxp.z .. " } "
		)
	end
	result1, result2 =  minetest_find_nodes_in_area(
		{ x=clamp_s16(minp.x), y=clamp_s16(minp.y), z=clamp_s16(minp.z) },
		{ x=clamp_s16(maxp.x), y=clamp_s16(maxp.y), z=clamp_s16(maxp.z) },
		...
	)
	return result1, result2
end

minetest.find_nodes_in_area3 = function(minp, maxp, ...)
	local result1, result2
	local clamped_minp = vector.apply(minp, clamp_s16)
	local clamped_maxp = vector.apply(maxp, clamp_s16)
	if not (vector.equals(clamped_minp, minp) and vector.equals(clamped_maxp, maxp)) then
		minetest.log(
			"warning",
			"find_nodes_in_area() called with coords outside interval (-32768, 32767), clamping arguments: " ..
			"minp { x=" .. minp.x .. ", y=" .. minp.y .. " z=" .. minp.z .. " } " ..
			"maxp { x=" .. maxp.x .. ", y=" .. maxp.y .. " z=" .. maxp.z .. " } "
		)
		result1, result2 = minetest_find_nodes_in_area(
			{ x=clamp_s16(minp.x), y=clamp_s16(minp.y), z=clamp_s16(minp.z) },
			{ x=clamp_s16(maxp.x), y=clamp_s16(maxp.y), z=clamp_s16(maxp.z) },
			...
		)
	else
		result1, result2 = minetest_find_nodes_in_area(
			minp,
			maxp,
			...
		)
	end
	return result1, result2
end

minetest.register_chatcommand("areatest1", {
func = function(name)
start = os.clock()
for x = 32767, 32700, -10 do
	for y = -32768, -32700, 10 do
		for z = 32760, 32770, 0.1 do
			local radius = 8
			local pos = { x=x, y=y, z=z }
			local areamin = vector.subtract(pos, radius)
			local areamax = vector.add(pos, radius)
			local npos, nnum = minetest.find_nodes_in_area1(
			   areamin,
			   areamax,
			   "air"
			)
		end
	end
end
fin = os.clock()
minetest.debug(fin - start)
end
})

minetest.register_chatcommand("areatest2", {
func = function(name)
start = os.clock()
for x = 32767, 32700, -10 do
	for y = -32768, -32700, 10 do
		for z = 32760, 32770, 0.1 do
			local radius = 8
			local pos = { x=x, y=y, z=z }
			local areamin = vector.subtract(pos, radius)
			local areamax = vector.add(pos, radius)
			local npos, nnum = minetest.find_nodes_in_area2(
			   areamin,
			   areamax,
			   "air"
			)
		end
	end
end
fin = os.clock()
minetest.debug(fin - start)
end
})

minetest.register_chatcommand("areatest3", {
func = function(name)
start = os.clock()
for x = 32767, 32700, -10 do
	for y = -32768, -32700, 10 do
		for z = 32760, 32770, 0.1 do
			local radius = 8
			local pos = { x=x, y=y, z=z }
			local areamin = vector.subtract(pos, radius)
			local areamax = vector.add(pos, radius)
			local npos, nnum = minetest.find_nodes_in_area3(
			   areamin,
			   areamax,
			   "air"
			)
		end
	end
end
fin = os.clock()
minetest.debug(fin - start)
end
})
@cora do these three work? If so, is there a difference in runtime? ``` function clamp(value, min, max) assert( min < max ) if value < min then return min end if value > max then return max end return value end assert( clamp(000, -100, 100) == 000 ) assert( clamp(999, -100, 100) == 100 ) assert( clamp(999, -999, 999) == 999 ) assert( clamp(998, 999, 1999) == 999 ) assert( clamp(999, 999, 1999) == 999 ) function clamp_s16(value) -- seems minetest hangs on -32768 and 32767 return clamp(value, -32767, 32766) end assert( clamp_s16(000000) == 000000 ) assert( clamp_s16(000001) == 000001 ) assert( clamp_s16(000010) == 000010 ) assert( clamp_s16(000100) == 000100 ) assert( clamp_s16(001000) == 001000 ) assert( clamp_s16(010000) == 010000 ) assert( clamp_s16(100000) == 032766 ) assert( clamp_s16(-00000) == -00000 ) assert( clamp_s16(-00009) == -00009 ) assert( clamp_s16(-00099) == -00099 ) assert( clamp_s16(-00999) == -00999 ) assert( clamp_s16(-09999) == -09999 ) assert( clamp_s16(-99999) == -32767 ) minetest_find_nodes_in_area = minetest.find_nodes_in_area minetest.find_nodes_in_area1 = function(minp, maxp, ...) local result1, result2 if minp.x >= 32767 or minp.x <= -32768 or minp.y >= 32767 or minp.y <= -32768 or minp.z >= 32767 or minp.z <= -32768 or maxp.x >= 32767 or maxp.x <= -32768 or maxp.y >= 32767 or maxp.y <= -32768 or maxp.z >= 32767 or maxp.z <= -32768 then minetest.log( "warning", "find_nodes_in_area() called with coords outside interval (-32768, 32767), clamping arguments: " .. "minp { x=" .. minp.x .. ", y=" .. minp.y .. " z=" .. minp.z .. " } " .. "maxp { x=" .. maxp.x .. ", y=" .. maxp.y .. " z=" .. maxp.z .. " } " ) result1, result2 = minetest_find_nodes_in_area( { x=clamp_s16(minp.x), y=clamp_s16(minp.y), z=clamp_s16(minp.z) }, { x=clamp_s16(maxp.x), y=clamp_s16(maxp.y), z=clamp_s16(maxp.z) }, ... ) else result1, result2 = minetest_find_nodes_in_area( minp, maxp, ... ) end end minetest.find_nodes_in_area2 = function(minp, maxp, ...) local result1, result2 local clamped_minp = vector.apply(minp, clamp_s16) local clamped_maxp = vector.apply(maxp, clamp_s16) if not (vector.equals(clamped_minp, minp) and vector.equals(clamped_maxp, maxp)) then minetest.log( "warning", "find_nodes_in_area() called with coords outside interval (-32768, 32767), clamping arguments: " .. "minp { x=" .. minp.x .. ", y=" .. minp.y .. " z=" .. minp.z .. " } " .. "maxp { x=" .. maxp.x .. ", y=" .. maxp.y .. " z=" .. maxp.z .. " } " ) end result1, result2 = minetest_find_nodes_in_area( { x=clamp_s16(minp.x), y=clamp_s16(minp.y), z=clamp_s16(minp.z) }, { x=clamp_s16(maxp.x), y=clamp_s16(maxp.y), z=clamp_s16(maxp.z) }, ... ) return result1, result2 end minetest.find_nodes_in_area3 = function(minp, maxp, ...) local result1, result2 local clamped_minp = vector.apply(minp, clamp_s16) local clamped_maxp = vector.apply(maxp, clamp_s16) if not (vector.equals(clamped_minp, minp) and vector.equals(clamped_maxp, maxp)) then minetest.log( "warning", "find_nodes_in_area() called with coords outside interval (-32768, 32767), clamping arguments: " .. "minp { x=" .. minp.x .. ", y=" .. minp.y .. " z=" .. minp.z .. " } " .. "maxp { x=" .. maxp.x .. ", y=" .. maxp.y .. " z=" .. maxp.z .. " } " ) result1, result2 = minetest_find_nodes_in_area( { x=clamp_s16(minp.x), y=clamp_s16(minp.y), z=clamp_s16(minp.z) }, { x=clamp_s16(maxp.x), y=clamp_s16(maxp.y), z=clamp_s16(maxp.z) }, ... ) else result1, result2 = minetest_find_nodes_in_area( minp, maxp, ... ) end return result1, result2 end minetest.register_chatcommand("areatest1", { func = function(name) start = os.clock() for x = 32767, 32700, -10 do for y = -32768, -32700, 10 do for z = 32760, 32770, 0.1 do local radius = 8 local pos = { x=x, y=y, z=z } local areamin = vector.subtract(pos, radius) local areamax = vector.add(pos, radius) local npos, nnum = minetest.find_nodes_in_area1( areamin, areamax, "air" ) end end end fin = os.clock() minetest.debug(fin - start) end }) minetest.register_chatcommand("areatest2", { func = function(name) start = os.clock() for x = 32767, 32700, -10 do for y = -32768, -32700, 10 do for z = 32760, 32770, 0.1 do local radius = 8 local pos = { x=x, y=y, z=z } local areamin = vector.subtract(pos, radius) local areamax = vector.add(pos, radius) local npos, nnum = minetest.find_nodes_in_area2( areamin, areamax, "air" ) end end end fin = os.clock() minetest.debug(fin - start) end }) minetest.register_chatcommand("areatest3", { func = function(name) start = os.clock() for x = 32767, 32700, -10 do for y = -32768, -32700, 10 do for z = 32760, 32770, 0.1 do local radius = 8 local pos = { x=x, y=y, z=z } local areamin = vector.subtract(pos, radius) local areamax = vector.add(pos, radius) local npos, nnum = minetest.find_nodes_in_area3( areamin, areamax, "air" ) end end end fin = os.clock() minetest.debug(fin - start) end }) ```
Author
Member

the results of the different /areatests vary a lot: they were between 1.2 and 1.8 and 1 mostly seemed to be the fastest but it was also the slowest sometimes.

the results of the different /areatests vary a lot: they were between 1.2 and 1.8 and 1 mostly seemed to be the fastest but it was also the slowest sometimes.
cora added
confirmed
and removed
unconfirmed
labels 2021-11-14 15:50:24 +01:00
Author
Member

fixed by #169

fixed by #169
cora closed this issue 2021-11-18 12:44:33 +01:00
This repo is archived. You cannot comment on issues.
No Milestone
No project
No Assignees
2 Participants
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: Mineclonia/Mineclonia#165
No description provided.