Browse files

Intersects_protection(): Move from Minetest Game to builtin (#6952)

A useful function that applies 'core.is_protected()' to a 3D lattice of
points evenly spaced throughout a defined volume, with a parameter for
the maximum spacing of points.
  • Loading branch information...
paramat authored and nerzhul committed Jan 23, 2018
1 parent 0425c6b commit 01bc817fe0a59e2e2b20c26d395c5b989c5156c9
Showing with 75 additions and 1 deletion.
  1. +65 −1 builtin/game/misc.lua
  2. +10 −0 doc/lua_api.txt
@@ -40,14 +40,17 @@ function core.check_player_privs(name, ...)
return true, ""

local player_list = {}

function core.send_join_message(player_name)
if not minetest.is_singleplayer() then
core.chat_send_all("*** " .. player_name .. " joined the game.")

function core.send_leave_message(player_name, timed_out)
local announcement = "*** " .. player_name .. " left the game."
if timed_out then
@@ -56,18 +59,21 @@ function core.send_leave_message(player_name, timed_out)

local player_name = player:get_player_name()
player_list[player_name] = player

core.register_on_leaveplayer(function(player, timed_out)
local player_name = player:get_player_name()
player_list[player_name] = nil
core.send_leave_message(player_name, timed_out)

function core.get_connected_players()
local temp_table = {}
for index, value in pairs(player_list) do
@@ -78,12 +84,15 @@ function core.get_connected_players()
return temp_table

function minetest.player_exists(name)
return minetest.get_auth_handler().get_auth(name) ~= nil

-- Returns two position vectors representing a box of `radius` in each
-- direction centered around the player corresponding to `player_name`

function core.get_player_radius_area(player_name, radius)
local player = core.get_player_by_name(player_name)
if player == nil then
@@ -101,10 +110,12 @@ function core.get_player_radius_area(player_name, radius)
return p1, p2

function core.hash_node_position(pos)
return (pos.z+32768)*65536*65536 + (pos.y+32768)*65536 + pos.x+32768

function core.get_position_from_hash(hash)
local pos = {}
pos.x = (hash%65536) - 32768
@@ -115,6 +126,7 @@ function core.get_position_from_hash(hash)
return pos

function core.get_item_group(name, group)
if not core.registered_items[name] or not
core.registered_items[name].groups[group] then
@@ -123,11 +135,13 @@ function core.get_item_group(name, group)
return core.registered_items[name].groups[group]

function core.get_node_group(name, group)
core.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead")
return core.get_item_group(name, group)

function core.setting_get_pos(name)
local value = core.settings:get(name)
if not value then
@@ -136,17 +150,64 @@ function core.setting_get_pos(name)
return core.string_to_pos(value)

-- To be overriden by protection mods

function core.is_protected(pos, name)
return false

function core.record_protection_violation(pos, name)
for _, func in pairs(core.registered_on_protection_violation) do
func(pos, name)

-- Checks if specified volume intersects a protected volume

function core.intersects_protection(minp, maxp, player_name, interval)
-- 'interval' is the largest allowed interval for the 3D lattice of checks.

-- Compute the optimal float step 'd' for each axis so that all corners and
-- borders are checked. 'd' will be smaller or equal to 'interval'.
-- Subtracting 1e-4 ensures that the max co-ordinate will be reached by the
-- for loop (which might otherwise not be the case due to rounding errors).

-- Default to 4
interval = interval or 4
local d = {}

for _, c in pairs({"x", "y", "z"}) do
if maxp[c] > minp[c] then
d[c] = (maxp[c] - minp[c]) /
math.ceil((maxp[c] - minp[c]) / interval) - 1e-4
elseif maxp[c] == minp[c] then
d[c] = 1 -- Any value larger than 0 to avoid division by zero
else -- maxp[c] < minp[c], print error and treat as protection intersected
minetest.log("error", "maxp < minp in 'minetest.intersects_protection()'")
return true

for zf = minp.z, maxp.z, d.z do
local z = math.floor(zf + 0.5)
for yf = minp.y, maxp.y, d.y do
local y = math.floor(yf + 0.5)
for xf = minp.x, maxp.x, d.x do
local x = math.floor(xf + 0.5)
if core.is_protected({x = x, y = y, z = z}, player_name) then
return true

return false

local raillike_ids = {}
local raillike_cur_id = 0
function core.raillike_group(name)
@@ -159,7 +220,9 @@ function core.raillike_group(name)
return id

-- HTTP callback interface

function core.http_add_fetch(httpenv)
httpenv.fetch = function(req, callback)
local handle = httpenv.fetch_async(req)
@@ -178,11 +241,12 @@ function core.http_add_fetch(httpenv)
return httpenv

function core.close_formspec(player_name, formname)
return minetest.show_formspec(player_name, formname, "")

function core.cancel_shutdown_requests()
core.request_shutdown("", false, -1)

@@ -3413,6 +3413,16 @@ These functions return the leftover itemstack.
* `minetest.record_protection_violation(pos, name)`
* This function calls functions registered with
* `minetest.intersects_protection(minp, maxp, player_name, interval)
* Returns a boolean, returns true if the volume defined by `minp` and `maxp`
intersects a protected area not owned by `player_name`.
* Applies `is_protected()` to a 3D lattice of points in the defined volume.
The points are spaced evenly throughout the volume and have a spacing
similar to, but no larger than, `interval`.
* All corners and edges of the defined volume are checked.
* `interval` defaults to 4.
* `interval` should be carefully chosen and maximised to avoid an excessive
number of points being checked.
* `minetest.rotate_and_place(itemstack, placer, pointed_thing, infinitestacks, orient_flags)`
* Attempt to predict the desired orientation of the facedir-capable node
defined by `itemstack`, and place it accordingly (on-wall, on the floor, or

5 comments on commit 01bc817


This comment has been minimized.

Copy link

sfan5 replied Jan 23, 2018

Wouldn't it have been better to name this minetest.is_area_protected and encourage protection mods to overwrite it with a more efficient implementation (if possible)?


This comment has been minimized.

Copy link

SmallJoker replied Jan 23, 2018

Both versions sound good. It's a function that will do its job just fine with any area-based protection system without requiring any modifcation. All builtin/core functions can be overwritten anyway if that's desired.


This comment has been minimized.

Copy link

sfan5 replied Jan 23, 2018

Explicitly mentioning that you are supposed to overwrite this function if possible would still be nice.


This comment has been minimized.

Copy link

rubenwardy replied Jan 23, 2018

I agree with @sfan5
I also makes the function name a verb, which is better code style


This comment has been minimized.

Copy link
Member Author

paramat replied Jan 29, 2018

'is area protected' is misleading and less descriptive, it's actually 'does defined volume intersect any protected volume'.
The function name is a verb and makes sense:
(pos) is protected? true/false
(volume) intersects protection? true/false

Explicitly mentioning that you are supposed to overwrite this function if possible would still be nice.

It's not 'supposed' to be overriden as it is functional code, but it can be overriden if desired or if more efficient to do so in a protection mod.
'is protected' is supposed to be overriden because it is an empty function.

So, it's ok to add a comment:
"This function can be overriden in a protection mod if desired or if more efficient"
I can do this.
All else i disagree with.

Please sign in to comment.