Skip to content

Commit

Permalink
Intersects_protection(): Move from Minetest Game to builtin (#6952)
Browse files Browse the repository at this point in the history
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 01bc817
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 1 deletion.
66 changes: 65 additions & 1 deletion builtin/game/misc.lua
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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

10 changes: 10 additions & 0 deletions doc/lua_api.txt
Expand Up @@ -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
Expand Down

5 comments on commit 01bc817

@sfan5
Copy link
Member

@sfan5 sfan5 commented on 01bc817 Jan 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

@SmallJoker SmallJoker commented on 01bc817 Jan 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

@sfan5 sfan5 commented on 01bc817 Jan 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@rubenwardy
Copy link
Member

@rubenwardy rubenwardy commented on 01bc817 Jan 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@paramat
Copy link
Contributor Author

@paramat paramat commented on 01bc817 Jan 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'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.