Permalink
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, ""
end


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.")
end
end


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)
core.chat_send_all(announcement)
end


core.register_on_joinplayer(function(player)
local player_name = player:get_player_name()
player_list[player_name] = player
core.send_join_message(player_name)
end)


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)
end)


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
end


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


-- 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
end


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


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
end


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]
end


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)
end


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)
end


-- To be overriden by protection mods

function core.is_protected(pos, name)
return false
end


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


-- 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
end
end

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
end
end
end
end

return false
end


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


-- 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
end


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


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

@@ -3413,6 +3413,16 @@ These functions return the leftover itemstack.
* `minetest.record_protection_violation(pos, name)`
* This function calls functions registered with
`minetest.register_on_protection_violation`.
* `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

@sfan5

This comment has been minimized.

Copy link
Member

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)?

@SmallJoker

This comment has been minimized.

Copy link
Member

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.

@sfan5

This comment has been minimized.

Copy link
Member

sfan5 replied Jan 23, 2018

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

@rubenwardy

This comment has been minimized.

Copy link
Member

rubenwardy replied Jan 23, 2018

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

@paramat

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.