mirror of
https://github.com/Uberi/Minetest-WorldEdit.git
synced 2024-09-27 15:10:17 +02:00
Merge ede4c76db4
into 2c4a791805
This commit is contained in:
commit
f8a15876a9
|
@ -1 +1,2 @@
|
||||||
worldedit
|
worldedit
|
||||||
|
worldedit_protection
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
38
worldedit_protection/README.txt
Normal file
38
worldedit_protection/README.txt
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
Because worldedit can easily cause massive destruction when in the wrong hands, the only use it has seen outside of singleplayer games is when wielded by an admin or moderator.
|
||||||
|
I (khonkhortisan) intend to change that, by tying its use to, and making it respect, owned areas:
|
||||||
|
On a non-creative server (say, survival) it does not make sense to be able to create valuable ores out of thin air, so the "worldedit" privilege is required to do anything.
|
||||||
|
On a creative server that does not have any form of land ownership, the "worldedit" privilege is required to do anything. A malicious player is limited by the speed of their arm.
|
||||||
|
On a creative server that has an ownership mod (say, areas) the ability to use worldedit is more complicated than a simple "yes" or "no":
|
||||||
|
Commands that do not actually edit nodes can be used by anyone. This includes setting the region, inspecting nodes, saving (to a standard filename, see below) and copying (the initial region with //copy or //stack)
|
||||||
|
Commands that do edit nodes check the current worldedit region (or the bounding box for the command) for the player's ability both to edit manually, and through this mod: (this includes the second region with //copy or //stack)
|
||||||
|
If the region is completely contained by area assigned to you, the command succeeds.
|
||||||
|
If the whole region is not owned by anyone (with or without part of it being owned by you):
|
||||||
|
If you do not have the "worldedit" privilege, you can only edit area you fully own, and the command fails.
|
||||||
|
If you do have the "worldedit" privilege, you can edit unowned area and/or your area (but not someone else's area), and the command succeeds.
|
||||||
|
If part of the region is owned by someone else (with or without part of it being unowned, or owned by you):
|
||||||
|
If you have the "areas" privilege, you could make yourself the owner anyway, so the command succeeds.
|
||||||
|
If you do not have the "areas" privilege, the land cannot be edited by you (worldedit does not currently understand owned but shared area?)
|
||||||
|
File writes - if you can set a region, you can read from the world.
|
||||||
|
If you have the "server" privilege, you can save to an arbitrary filename, as usual.
|
||||||
|
If you do not have the "server" privilege, the filename is forced to "worldedit.we" or "worldedit.mts".
|
||||||
|
File reads (of *.we or *.mts, of course) will succeed, but must pass the region write check.
|
||||||
|
Lua commands (//lua and //luatransform) require first "server", then "admin", as usual.
|
||||||
|
|
||||||
|
minetest.register_chatcommand
|
||||||
|
privs = {worldedit}, is what it used to have, but that privilege is only required in certain cases (see above)
|
||||||
|
privs = {}, func = worldedit.privs(function(name, param)...end), passes to func a wrapper that checks first whether the worldedit privilege is required, then if the player has it, before running the inner function.
|
||||||
|
|
||||||
|
worldedit.privs can also be used in an if statement, as it returns both whether execution should continue (nil or 1), and whether a region permission check should be skipped (2) due to the areas privilege.
|
||||||
|
|
||||||
|
worldedit.can_edit_volume
|
||||||
|
It takes the player's name, then a list of either two or four points (two points per region, //move has two regions to check)
|
||||||
|
It re-runs worldedit.privs to attempt skipping a region permission check (if the player has the "areas" privilege)
|
||||||
|
This return value should probably be cached instead of running the function twice.
|
||||||
|
func = worldedit.privs(safe_region(function(name, param)...if worldedit.can_edit_volume(name, {pos1, pos2}) then...
|
||||||
|
The "wp" return value has to make it past safe_region somehow.
|
||||||
|
It returns whether the command should actually be run, along with telling the player why it wasn't.
|
||||||
|
|
||||||
|
minetest.allocate_schematic
|
||||||
|
A function for schematics corresponding with worldedit.allocate for .we files
|
||||||
|
|
||||||
|
|
2
worldedit_protection/depends.txt
Normal file
2
worldedit_protection/depends.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
worldedit
|
||||||
|
commonlib
|
178
worldedit_protection/init.lua
Normal file
178
worldedit_protection/init.lua
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
--if there's no protection mod, no worldedit means no editing, worldedit means editing anywhere (old behaviour)
|
||||||
|
--if there's a protection mod (and it's creative mode), no worldedit means editing only in your area, worldedit means editing in no-man's land too, and areas means editing anywhere.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
--let the other mod load first
|
||||||
|
minetest.after(0, function()
|
||||||
|
--I would use mod.soft_depend from commonlib, but there are multiple mods that could create owned land
|
||||||
|
PROTECTION_MOD_EXISTS = minetest.is_protected ~= old_is_protected
|
||||||
|
--else fall back to old behaviour, where
|
||||||
|
--worldedit privilege is permission to edit everything
|
||||||
|
end)
|
||||||
|
--]]
|
||||||
|
local PROTECTION_MOD_EXISTS = false
|
||||||
|
mod.soft_depend("areas", function()
|
||||||
|
PROTECTION_MOD_EXISTS = true
|
||||||
|
end)
|
||||||
|
--mod.soft_depend("other protection mod goes here", function()
|
||||||
|
-- PROTECTION_MOD_EXISTS = true
|
||||||
|
--end)
|
||||||
|
|
||||||
|
--[[
|
||||||
|
worldedit.privs replaces privs = {worldedit = true}, and also helps bypass volume permission checks.
|
||||||
|
Usage:
|
||||||
|
In chatcommand:
|
||||||
|
privs = {}
|
||||||
|
func = worldedit.privs(function(name, param)...end)
|
||||||
|
|
||||||
|
In if statement:
|
||||||
|
name = minetest.get_player_name(node)
|
||||||
|
if worldedit.privs() then
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
nil ( false) for no permission to worldedit anywhere,
|
||||||
|
1 ( true ) for permission to worldedit at least somewhere, and
|
||||||
|
2 (extra true ) for worldediting everywhere without checking for further permission.
|
||||||
|
--]]
|
||||||
|
--I wanted this function to directly choose the privileges for the chat command, but it only applies once.
|
||||||
|
--privs={worldedit=true [, server=true]}
|
||||||
|
--privs={worldedit=worldedit.priv() [, server=true]}
|
||||||
|
--instead, I had to wrap the rest of func = .
|
||||||
|
worldedit.privs = function(privsfunc) --returns a function (the argument function wrapped in another) which takes the arguments (name, param).
|
||||||
|
if privsfunc == nil then
|
||||||
|
privsfunc = function() --[[no-op]] end
|
||||||
|
end
|
||||||
|
|
||||||
|
--this silly syntax was copied from safe_region, which is actually executed on chatcommand registration, and must return a function instead of the result of a function.
|
||||||
|
--The innermost anonymous function is declared. Then safe_region executes, adding a function wrapper around that function. Then worldedit.privs gets that as an argument, and adds another wrapper. The doubly-wrapped function is the one registered as a chatcommand.
|
||||||
|
return function(name, param)
|
||||||
|
if minetest.check_player_privs(name, {areas=true}) then
|
||||||
|
--You can set areas, so you are allowed to worldedit them too.
|
||||||
|
--The ability to set the whole world as owned by yourself is already potentially destructive, what's more destructive capability?
|
||||||
|
privsfunc(name, param)
|
||||||
|
return 2 --edit everywhere without checks
|
||||||
|
end
|
||||||
|
local is_creative = minetest.setting_getbool("creative_mode")
|
||||||
|
if not is_creative or not PROTECTION_MOD_EXISTS then
|
||||||
|
--no protection mod, or not the kind of world where people can just create nodes out of thin air,
|
||||||
|
--worldedit privilege means editing anywhere
|
||||||
|
if minetest.check_player_privs(name, {worldedit=true}) then
|
||||||
|
privsfunc(name, param)
|
||||||
|
return 2 --edit everywhere without checks
|
||||||
|
else
|
||||||
|
--default chatcommand failure message
|
||||||
|
minetest.chat_send_player(name, "You don't have permission to run this command (missing privileges: worldedit)\nReasons:".. (is_creative and "" or " (not creative mode)") .. (PROTECTION_MOD_EXISTS and "" or " (no protection mod)"))
|
||||||
|
--func(name, param) placeholder
|
||||||
|
return nil --edit nowhere
|
||||||
|
end
|
||||||
|
else
|
||||||
|
--protection mod, can edit inside your area without worldedit privilege
|
||||||
|
--(worldedit and areas let you edit in no-man's land and other-owned area)
|
||||||
|
privsfunc(name, param)
|
||||||
|
return 1 --edit at least somewhere, with checks
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--this is... within chatcommands that actually change land
|
||||||
|
--(should be the same functions as safe_region)
|
||||||
|
--also check for permission when region is set? no, //stack goes outside the boundaries.
|
||||||
|
--so the region is defined per-command on exec.
|
||||||
|
--//move has disconnected sections, so it's passed as a list of points.
|
||||||
|
--which are deduplicated.
|
||||||
|
worldedit.can_edit_volume = function(name, volume) --does not return a function like .privs does
|
||||||
|
--debug info, remove after testing
|
||||||
|
minetest.chat_send_player(name, minetest.pos_to_string(volume[1])..minetest.pos_to_string(volume[2]))
|
||||||
|
if volume[3] ~= nil then
|
||||||
|
minetest.chat_send_player(name, minetest.pos_to_string(volume[3])..minetest.pos_to_string(volume[4]))
|
||||||
|
end
|
||||||
|
|
||||||
|
--volume is before func, unlike safe_region having func before count
|
||||||
|
--because func may be removed to have can_edit_volume in an if statement
|
||||||
|
--like worldedit.privs can be
|
||||||
|
|
||||||
|
--worldedit.privs was run once to prevent safe_region large area warnings,
|
||||||
|
--then safe_region was run to prevent unnecessary large-scale permission checks
|
||||||
|
--then can_edit_volume is run before //set to honor areas
|
||||||
|
--then worldedit.privs is run again to attempt skipping checks (resusing the same code)
|
||||||
|
--then set is finally run.
|
||||||
|
|
||||||
|
--<==>--local returnfunc = function(name, param)
|
||||||
|
--worldedit.privs said that 'name' can use worldedit at least somewhere
|
||||||
|
-- return value 1 (or 2) before this function was run.
|
||||||
|
|
||||||
|
--Try skipping volume permission checks.
|
||||||
|
local wp = worldedit.privs(nil)(name, nil)
|
||||||
|
if wp == 2 then
|
||||||
|
--volfunc(name, param)
|
||||||
|
return true
|
||||||
|
elseif wp == nil then
|
||||||
|
--safety feature in case worldedit.can_edit_volume is ever run alone, without being surrounded by worldedit.privs()
|
||||||
|
--Shouldn't ever get here.
|
||||||
|
--Any volume-changing function is surrounded by this, then safe_region, then worldedit.privs()
|
||||||
|
--volfunc(name, param) placeholder
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[I need to use a special per-command region (think /stack, or even worse, /move), the same one safe_region uses, but corner points instead of count... instead of a for loop interpolating between pos1 and pos2]]--
|
||||||
|
|
||||||
|
--cache the result of this function for later
|
||||||
|
local has_worldedit = minetest.check_player_privs(name, {worldedit=true})
|
||||||
|
|
||||||
|
--all or nothing here
|
||||||
|
--volume may be a single region (//set) or two possibly overlapping regions (//move)
|
||||||
|
--an overlapping //move is checked twice - any attempt to make it more efficient (by combining the volumes into a single two-pos one) may take into account that the format is //move axis amount, and break if the format is changed to //move xamount yamount zamount
|
||||||
|
for v = 1,3,2 do --volume[1] and volume[2]
|
||||||
|
if volume[v] ~= nil then --volume[3] and volume[4]
|
||||||
|
volume[v], volume[v+1] = worldedit.sort_pos(volume[v], volume[v+1])
|
||||||
|
for y = volume[v].y, volume[v+1].y do
|
||||||
|
for z = volume[v].z, volume[v+1].z do
|
||||||
|
for x = volume[v].x, volume[v+1].x do
|
||||||
|
local node = {x=x, y=y, z=z}
|
||||||
|
--[[
|
||||||
|
THIS SECTION IGNORES the distinction of area that is owned by someone else, but still editable
|
||||||
|
this is treated as area owned by the editor, or no-man's land depending on if it's shared with one person or everyone
|
||||||
|
|
||||||
|
If it was treated differently (it's not), then single edits would not be able to cross the border between someone else's editable land, and no-man's land, to prevent accidental writes. It may cross the border between multiple people's editable land (or should it?), such as to create a bridge between two skyscrapers that were previously built separately.
|
||||||
|
|
||||||
|
This needs testing for the other changes first.
|
||||||
|
--]]
|
||||||
|
|
||||||
|
--Is it owned?
|
||||||
|
if minetest.is_protected(node, "") then
|
||||||
|
--Is it someone else's?
|
||||||
|
if minetest.is_protected(node, name) then
|
||||||
|
--already checked the ability to make it mine (areas)
|
||||||
|
minetest.chat_send_player(name, "You don't have permission to run this command "..
|
||||||
|
"(Region "..minetest.pos_to_string(volume[1]).."-"..minetest.pos_to_string(volume[2])..
|
||||||
|
(volume[3] ~= nil and ("+"..minetest.pos_to_string(volume[3]).."-"..minetest.pos_to_string(volume[4])) or "")..
|
||||||
|
" overlaps (first match) areaname: (x, y, z)-(x, y, z) owned by playername)"..
|
||||||
|
"\nReasons: (missing areas privilege)"
|
||||||
|
)
|
||||||
|
--volfunc(name, param) placeholder
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
--it's mine
|
||||||
|
--continue
|
||||||
|
|
||||||
|
--no-man's land
|
||||||
|
--can I edit that?
|
||||||
|
elseif not has_worldedit then --using cached check
|
||||||
|
minetest.chat_send_player(name, "You don't have permission to run this command (missing worldedit privilege)\nReasons: (At least part of this area is unowned (not owned by you)) (missing areas privilege)")
|
||||||
|
--volfunc(name, param) placeholder
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end -- for x
|
||||||
|
end -- for z
|
||||||
|
end -- for y
|
||||||
|
end -- if not nil
|
||||||
|
end -- for v
|
||||||
|
|
||||||
|
--the whole thing is
|
||||||
|
--a) owned by me, and/or
|
||||||
|
--b) owned by no one and I have the worldedit privilege.
|
||||||
|
--c) I have the areas privilege and it's possibly owned by someone else. (returned earlier)
|
||||||
|
--volfunc(name, param)
|
||||||
|
return true
|
||||||
|
--<==>--end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user