aboutsummaryrefslogtreecommitdiff
path: root/builtin/game
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/game')
-rw-r--r--builtin/game/auth.lua2
-rw-r--r--builtin/game/chatcommands.lua166
-rw-r--r--builtin/game/constants.lua17
-rw-r--r--builtin/game/deprecated.lua16
-rw-r--r--builtin/game/falling.lua176
-rw-r--r--builtin/game/features.lua7
-rw-r--r--builtin/game/init.lua4
-rw-r--r--builtin/game/item.lua89
-rw-r--r--builtin/game/item_entity.lua24
-rw-r--r--builtin/game/misc.lua157
-rw-r--r--builtin/game/privileges.lua1
-rw-r--r--builtin/game/register.lua34
-rw-r--r--builtin/game/static_spawn.lua22
13 files changed, 461 insertions, 254 deletions
diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua
index 423eb3134..deb811b14 100644
--- a/builtin/game/auth.lua
+++ b/builtin/game/auth.lua
@@ -20,7 +20,7 @@ function core.privs_to_string(privs, delim)
local list = {}
for priv, bool in pairs(privs) do
if bool then
- table.insert(list, priv)
+ list[#list + 1] = priv
end
end
return table.concat(list, delim)
diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua
index 5d317de4b..3350140ee 100644
--- a/builtin/game/chatcommands.lua
+++ b/builtin/game/chatcommands.lua
@@ -51,6 +51,27 @@ core.register_on_chat_message(function(name, message)
return true -- Handled chat message
end)
+-- Parses a "range" string in the format of "here (number)" or
+-- "(x1, y1, z1) (x2, y2, z2)", returning two position vectors
+local function parse_range_str(player_name, str)
+ local p1, p2
+ local args = str:split(" ")
+
+ if args[1] == "here" then
+ p1, p2 = core.get_player_radius_area(player_name, tonumber(args[2]))
+ if p1 == nil then
+ return false, "Unable to get player " .. player_name .. " position"
+ end
+ else
+ p1, p2 = core.string_to_area(str)
+ if p1 == nil then
+ return false, "Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)"
+ end
+ end
+
+ return p1, p2
+end
+
--
-- Chat commands
--
@@ -63,6 +84,18 @@ core.register_chatcommand("me", {
end,
})
+core.register_chatcommand("admin", {
+ description = "Show the name of the server owner",
+ func = function(name)
+ local admin = minetest.setting_get("name")
+ if admin then
+ return true, "The administrator of this server is "..admin.."."
+ else
+ return false, "There's no administrator named in the config file."
+ end
+ end,
+})
+
core.register_chatcommand("help", {
privs = {},
params = "[all/privs/<cmd>]",
@@ -83,7 +116,7 @@ core.register_chatcommand("help", {
local cmds = {}
for cmd, def in pairs(core.chatcommands) do
if core.check_player_privs(name, def.privs) then
- table.insert(cmds, cmd)
+ cmds[#cmds + 1] = cmd
end
end
table.sort(cmds)
@@ -94,7 +127,7 @@ core.register_chatcommand("help", {
local cmds = {}
for cmd, def in pairs(core.chatcommands) do
if core.check_player_privs(name, def.privs) then
- table.insert(cmds, format_help_line(cmd, def))
+ cmds[#cmds + 1] = format_help_line(cmd, def)
end
end
table.sort(cmds)
@@ -102,7 +135,7 @@ core.register_chatcommand("help", {
elseif param == "privs" then
local privs = {}
for priv, def in pairs(core.registered_privileges) do
- table.insert(privs, priv .. ": " .. def.description)
+ privs[#privs + 1] = priv .. ": " .. def.description
end
table.sort(privs)
return true, "Available privileges:\n"..table.concat(privs, "\n")
@@ -148,8 +181,10 @@ core.register_chatcommand("grant", {
end
local privs = core.get_player_privs(grantname)
local privs_unknown = ""
+ local basic_privs =
+ core.string_to_privs(core.setting_get("basic_privs") or "interact,shout")
for priv, _ in pairs(grantprivs) do
- if priv ~= "interact" and priv ~= "shout" and
+ if not basic_privs[priv] and
not core.check_player_privs(name, {privs=true}) then
return false, "Your privileges are insufficient."
end
@@ -190,8 +225,10 @@ core.register_chatcommand("revoke", {
end
local revoke_privs = core.string_to_privs(revoke_priv_str)
local privs = core.get_player_privs(revoke_name)
+ local basic_privs =
+ core.string_to_privs(core.setting_get("basic_privs") or "interact,shout")
for priv, _ in pairs(revoke_privs) do
- if priv ~= "interact" and priv ~= "shout" and
+ if not basic_privs[priv] and
not core.check_player_privs(name, {privs=true}) then
return false, "Your privileges are insufficient."
end
@@ -315,10 +352,16 @@ core.register_chatcommand("teleport", {
p.x = tonumber(p.x)
p.y = tonumber(p.y)
p.z = tonumber(p.z)
- teleportee = core.get_player_by_name(name)
- if teleportee and p.x and p.y and p.z then
- teleportee:setpos(p)
- return true, "Teleporting to "..core.pos_to_string(p)
+ if p.x and p.y and p.z then
+ local lm = tonumber(minetest.setting_get("map_generation_limit") or 31000)
+ if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm or p.z < -lm or p.z > lm then
+ return false, "Cannot teleport out of map bounds!"
+ end
+ teleportee = core.get_player_by_name(name)
+ if teleportee then
+ teleportee:setpos(p)
+ return true, "Teleporting to "..core.pos_to_string(p)
+ end
end
local teleportee = nil
@@ -415,40 +458,65 @@ core.register_chatcommand("set", {
end,
})
-core.register_chatcommand("deleteblocks", {
+local function emergeblocks_callback(pos, action, num_calls_remaining, ctx)
+ if ctx.total_blocks == 0 then
+ ctx.total_blocks = num_calls_remaining + 1
+ ctx.current_blocks = 0
+ end
+ ctx.current_blocks = ctx.current_blocks + 1
+
+ if ctx.current_blocks == ctx.total_blocks then
+ core.chat_send_player(ctx.requestor_name,
+ string.format("Finished emerging %d blocks in %.2fms.",
+ ctx.total_blocks, (os.clock() - ctx.start_time) * 1000))
+ end
+end
+
+local function emergeblocks_progress_update(ctx)
+ if ctx.current_blocks ~= ctx.total_blocks then
+ core.chat_send_player(ctx.requestor_name,
+ string.format("emergeblocks update: %d/%d blocks emerged (%.1f%%)",
+ ctx.current_blocks, ctx.total_blocks,
+ (ctx.current_blocks / ctx.total_blocks) * 100))
+
+ core.after(2, emergeblocks_progress_update, ctx)
+ end
+end
+
+core.register_chatcommand("emergeblocks", {
params = "(here [radius]) | (<pos1> <pos2>)",
- description = "delete map blocks contained in area pos1 to pos2",
+ description = "starts loading (or generating, if inexistent) map blocks "
+ .. "contained in area pos1 to pos2",
privs = {server=true},
func = function(name, param)
- local p1 = {}
- local p2 = {}
- local args = param:split(" ")
- if args[1] == "here" then
- local player = core.get_player_by_name(name)
- if player == nil then
- core.log("error", "player is nil")
- return false, "Unable to get current position; player is nil"
- end
- p1 = player:getpos()
- p2 = p1
+ local p1, p2 = parse_range_str(name, param)
+ if p1 == false then
+ return false, p2
+ end
- if #args >= 2 then
- local radius = tonumber(args[2]) or 0
- p1 = vector.add(p1, radius)
- p2 = vector.subtract(p2, radius)
- end
- else
- local pos1, pos2 = unpack(param:split(") ("))
- if pos1 == nil or pos2 == nil then
- return false, "Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)"
- end
+ local context = {
+ current_blocks = 0,
+ total_blocks = 0,
+ start_time = os.clock(),
+ requestor_name = name
+ }
- p1 = core.string_to_pos(pos1 .. ")")
- p2 = core.string_to_pos("(" .. pos2)
+ core.emerge_area(p1, p2, emergeblocks_callback, context)
+ core.after(2, emergeblocks_progress_update, context)
- if p1 == nil or p2 == nil then
- return false, "Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)"
- end
+ return true, "Started emerge of area ranging from " ..
+ core.pos_to_string(p1, 1) .. " to " .. core.pos_to_string(p2, 1)
+ end,
+})
+
+core.register_chatcommand("deleteblocks", {
+ params = "(here [radius]) | (<pos1> <pos2>)",
+ description = "delete map blocks contained in area pos1 to pos2",
+ privs = {server=true},
+ func = function(name, param)
+ local p1, p2 = parse_range_str(name, param)
+ if p1 == false then
+ return false, p2
end
if core.delete_area(p1, p2) then
@@ -698,7 +766,7 @@ core.register_chatcommand("time", {
local hour = (current_time - minutes) / 60
return true, ("Current time is %d:%02d"):format(hour, minutes)
end
- local player_privs = minetest.get_player_privs(name)
+ local player_privs = core.get_player_privs(name)
if not player_privs.settime then
return false, "You don't have permission to run this command " ..
"(missing privilege: settime)."
@@ -727,6 +795,13 @@ core.register_chatcommand("time", {
end,
})
+core.register_chatcommand("days", {
+ description = "Display day count",
+ func = function(name, param)
+ return true, "Current day is " .. core.get_day_count()
+ end
+})
+
core.register_chatcommand("shutdown", {
description = "shutdown server",
privs = {server=true},
@@ -790,14 +865,25 @@ core.register_chatcommand("kick", {
})
core.register_chatcommand("clearobjects", {
+ params = "[full|quick]",
description = "clear all objects in world",
privs = {server=true},
func = function(name, param)
- core.log("action", name .. " clears all objects.")
+ local options = {}
+ if param == "" or param == "full" then
+ options.mode = "full"
+ elseif param == "quick" then
+ options.mode = "quick"
+ else
+ return false, "Invalid usage, see /help clearobjects."
+ end
+
+ core.log("action", name .. " clears all objects ("
+ .. options.mode .. " mode).")
core.chat_send_all("Clearing all objects. This may take long."
.. " You may experience a timeout. (by "
.. name .. ")")
- core.clear_objects()
+ core.clear_objects(options)
core.log("action", "Object clearing done.")
core.chat_send_all("*** Cleared all objects.")
end,
diff --git a/builtin/game/constants.lua b/builtin/game/constants.lua
new file mode 100644
index 000000000..d0b7c753c
--- /dev/null
+++ b/builtin/game/constants.lua
@@ -0,0 +1,17 @@
+-- Minetest: builtin/constants.lua
+
+--
+-- Constants values for use with the Lua API
+--
+
+-- Built-in Content IDs (for use with VoxelManip API)
+core.CONTENT_UNKNOWN = 125
+core.CONTENT_AIR = 126
+core.CONTENT_IGNORE = 127
+
+-- Block emerge status constants (for use with core.emerge_area)
+core.EMERGE_CANCELLED = 0
+core.EMERGE_ERRORED = 1
+core.EMERGE_FROM_MEMORY = 2
+core.EMERGE_FROM_DISK = 3
+core.EMERGE_GENERATED = 4
diff --git a/builtin/game/deprecated.lua b/builtin/game/deprecated.lua
index bbe68be3e..cd1cf5e2d 100644
--- a/builtin/game/deprecated.lua
+++ b/builtin/game/deprecated.lua
@@ -3,9 +3,8 @@
--
-- Default material types
--
-function digprop_err()
- core.log("info", debug.traceback())
- core.log("info", "WARNING: The core.digprop_* functions are obsolete and need to be replaced by item groups.")
+local function digprop_err()
+ core.log("deprecated", "The core.digprop_* functions are obsolete and need to be replaced by item groups.")
end
core.digprop_constanttime = digprop_err
@@ -16,12 +15,12 @@ core.digprop_woodlike = digprop_err
core.digprop_leaveslike = digprop_err
core.digprop_glasslike = digprop_err
-core.node_metadata_inventory_move_allow_all = function()
- core.log("info", "WARNING: core.node_metadata_inventory_move_allow_all is obsolete and does nothing.")
+function core.node_metadata_inventory_move_allow_all()
+ core.log("deprecated", "core.node_metadata_inventory_move_allow_all is obsolete and does nothing.")
end
-core.add_to_creative_inventory = function(itemstring)
- core.log('info', "WARNING: core.add_to_creative_inventory: This function is deprecated and does nothing.")
+function core.add_to_creative_inventory(itemstring)
+ core.log("deprecated", "core.add_to_creative_inventory: This function is deprecated and does nothing.")
end
--
@@ -32,7 +31,7 @@ local envref_deprecation_message_printed = false
setmetatable(core.env, {
__index = function(table, key)
if not envref_deprecation_message_printed then
- core.log("info", "WARNING: core.env:[...] is deprecated and should be replaced with core.[...]")
+ core.log("deprecated", "core.env:[...] is deprecated and should be replaced with core.[...]")
envref_deprecation_message_printed = true
end
local func = core[key]
@@ -50,4 +49,3 @@ setmetatable(core.env, {
function core.rollback_get_last_node_actor(pos, range, seconds)
return core.rollback_get_node_actions(pos, range, seconds, 1)[1]
end
-
diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua
index 58f68fc56..57bb98cfd 100644
--- a/builtin/game/falling.lua
+++ b/builtin/game/falling.lua
@@ -6,52 +6,49 @@
core.register_entity(":__builtin:falling_node", {
initial_properties = {
- physical = true,
- collide_with_objects = false,
- collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
visual = "wielditem",
+ visual_size = {x = 0.667, y = 0.667},
textures = {},
- visual_size = {x=0.667, y=0.667},
+ physical = true,
+ is_visible = false,
+ collide_with_objects = false,
+ collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
},
node = {},
set_node = function(self, node)
self.node = node
- local stack = ItemStack(node.name)
- local itemtable = stack:to_table()
- local itemname = nil
- if itemtable then
- itemname = stack:to_table().name
- end
- local item_texture = nil
- local item_type = ""
- if core.registered_items[itemname] then
- item_texture = core.registered_items[itemname].inventory_image
- item_type = core.registered_items[itemname].type
- end
- local prop = {
+ self.object:set_properties({
is_visible = true,
textures = {node.name},
- }
- self.object:set_properties(prop)
+ })
end,
get_staticdata = function(self)
- return self.node.name
+ return core.serialize(self.node)
end,
on_activate = function(self, staticdata)
- self.object:set_armor_groups({immortal=1})
- self:set_node({name=staticdata})
+ self.object:set_armor_groups({immortal = 1})
+
+ local node = core.deserialize(staticdata)
+ if node then
+ self:set_node(node)
+ elseif staticdata ~= "" then
+ self:set_node({name = staticdata})
+ end
end,
on_step = function(self, dtime)
- -- Set gravity
- self.object:setacceleration({x=0, y=-10, z=0})
+ -- Set gravity
+ local acceleration = self.object:getacceleration()
+ if not vector.equals(acceleration, {x = 0, y = -10, z = 0}) then
+ self.object:setacceleration({x = 0, y = -10, z = 0})
+ end
-- Turn to actual sand when collides to ground or just move
local pos = self.object:getpos()
- local bcp = {x=pos.x, y=pos.y-0.7, z=pos.z} -- Position of bottom center point
+ local bcp = {x = pos.x, y = pos.y - 0.7, z = pos.z} -- Position of bottom center point
local bcn = core.get_node(bcp)
local bcd = core.registered_nodes[bcn.name]
-- Note: walkable is in the node definition, not in item groups
@@ -62,7 +59,7 @@ core.register_entity(":__builtin:falling_node", {
if bcd and bcd.leveled and
bcn.name == self.node.name then
local addlevel = self.node.level
- if addlevel == nil or addlevel <= 0 then
+ if not addlevel or addlevel <= 0 then
addlevel = bcd.leveled
end
if core.add_node_level(bcp, addlevel) == 0 then
@@ -75,7 +72,7 @@ core.register_entity(":__builtin:falling_node", {
core.remove_node(bcp)
return
end
- local np = {x=bcp.x, y=bcp.y+1, z=bcp.z}
+ local np = {x = bcp.x, y = bcp.y + 1, z = bcp.z}
-- Check what's here
local n2 = core.get_node(np)
-- If it's not air or liquid, remove node and replace it with
@@ -86,25 +83,25 @@ core.register_entity(":__builtin:falling_node", {
if core.registered_nodes[n2.name].buildable_to == false then
-- Add dropped items
local drops = core.get_node_drops(n2.name, "")
- local _, dropped_item
for _, dropped_item in ipairs(drops) do
core.add_item(np, dropped_item)
end
end
-- Run script hook
- local _, callback
for _, callback in ipairs(core.registered_on_dignodes) do
- callback(np, n2, nil)
+ callback(np, n2)
end
end
-- Create node and remove entity
- core.add_node(np, self.node)
+ if core.registered_nodes[self.node.name] then
+ core.add_node(np, self.node)
+ end
self.object:remove()
nodeupdate(np)
return
end
local vel = self.object:getvelocity()
- if vector.equals(vel, {x=0,y=0,z=0}) then
+ if vector.equals(vel, {x = 0, y = 0, z = 0}) then
local npos = self.object:getpos()
self.object:setpos(vector.round(npos))
end
@@ -119,7 +116,7 @@ end
function drop_attached_node(p)
local nn = core.get_node(p).name
core.remove_node(p)
- for _,item in ipairs(core.get_node_drops(nn, "")) do
+ for _, item in ipairs(core.get_node_drops(nn, "")) do
local pos = {
x = p.x + math.random()/2 - 0.25,
y = p.y + math.random()/2 - 0.25,
@@ -131,25 +128,13 @@ end
function check_attached_node(p, n)
local def = core.registered_nodes[n.name]
- local d = {x=0, y=0, z=0}
+ local d = {x = 0, y = 0, z = 0}
if def.paramtype2 == "wallmounted" then
- if n.param2 == 0 then
- d.y = 1
- elseif n.param2 == 1 then
- d.y = -1
- elseif n.param2 == 2 then
- d.x = 1
- elseif n.param2 == 3 then
- d.x = -1
- elseif n.param2 == 4 then
- d.z = 1
- elseif n.param2 == 5 then
- d.z = -1
- end
+ d = core.wallmounted_to_dir(n.param2)
else
d.y = -1
end
- local p2 = {x=p.x+d.x, y=p.y+d.y, z=p.z+d.z}
+ local p2 = vector.add(p, d)
local nn = core.get_node(p2).name
local def2 = core.registered_nodes[nn]
if def2 and not def2.walkable then
@@ -162,10 +147,10 @@ end
-- Some common functions
--
-function nodeupdate_single(p, delay)
+function nodeupdate_single(p)
local n = core.get_node(p)
if core.get_item_group(n.name, "falling_node") ~= 0 then
- local p_bottom = {x=p.x, y=p.y-1, z=p.z}
+ local p_bottom = {x = p.x, y = p.y - 1, z = p.z}
local n_bottom = core.get_node(p_bottom)
-- Note: walkable is in the node definition, not in item groups
if core.registered_nodes[n_bottom.name] and
@@ -175,37 +160,88 @@ function nodeupdate_single(p, delay)
core.get_node_level(p_bottom) < core.get_node_max_level(p_bottom))) and
(not core.registered_nodes[n_bottom.name].walkable or
core.registered_nodes[n_bottom.name].buildable_to) then
- if delay then
- core.after(0.1, nodeupdate_single, {x=p.x, y=p.y, z=p.z}, false)
- else
- n.level = core.get_node_level(p)
- core.remove_node(p)
- spawn_falling_node(p, n)
- nodeupdate(p)
- end
+ n.level = core.get_node_level(p)
+ core.remove_node(p)
+ spawn_falling_node(p, n)
+ return true
end
end
if core.get_item_group(n.name, "attached_node") ~= 0 then
if not check_attached_node(p, n) then
drop_attached_node(p)
- nodeupdate(p)
+ return true
end
end
+
+ return false
end
-function nodeupdate(p, delay)
- -- Round p to prevent falling entities to get stuck
- p.x = math.floor(p.x+0.5)
- p.y = math.floor(p.y+0.5)
- p.z = math.floor(p.z+0.5)
+-- This table is specifically ordered.
+-- We don't walk diagonals, only our direct neighbors, and self.
+-- Down first as likely case, but always before self. The same with sides.
+-- Up must come last, so that things above self will also fall all at once.
+local nodeupdate_neighbors = {
+ {x = -1, y = -1, z = 0},
+ {x = 1, y = -1, z = 0},
+ {x = 0, y = -1, z = -1},
+ {x = 0, y = -1, z = 1},
+ {x = 0, y = -1, z = 0},
+ {x = -1, y = 0, z = 0},
+ {x = 1, y = 0, z = 0},
+ {x = 0, y = 0, z = 1},
+ {x = 0, y = 0, z = -1},
+ {x = 0, y = 0, z = 0},
+ {x = 0, y = 1, z = 0},
+}
- for x = -1,1 do
- for y = -1,1 do
- for z = -1,1 do
- nodeupdate_single({x=p.x+x, y=p.y+y, z=p.z+z}, delay or not (x==0 and y==0 and z==0))
- end
- end
+function nodeupdate(p)
+ -- Round p to prevent falling entities to get stuck.
+ p = vector.round(p)
+
+ -- We make a stack, and manually maintain size for performance.
+ -- Stored in the stack, we will maintain tables with pos, and
+ -- last neighbor visited. This way, when we get back to each
+ -- node, we know which directions we have already walked, and
+ -- which direction is the next to walk.
+ local s = {}
+ local n = 0
+ -- The neighbor order we will visit from our table.
+ local v = 1
+
+ while true do
+ -- Push current pos onto the stack.
+ n = n + 1
+ s[n] = {p = p, v = v}
+ -- Select next node from neighbor list.
+ p = vector.add(p, nodeupdate_neighbors[v])
+ -- Now we check out the node. If it is in need of an update,
+ -- it will let us know in the return value (true = updated).
+ if not nodeupdate_single(p) then
+ -- If we don't need to "recurse" (walk) to it then pop
+ -- our previous pos off the stack and continue from there,
+ -- with the v value we were at when we last were at that
+ -- node
+ repeat
+ local pop = s[n]
+ p = pop.p
+ v = pop.v
+ s[n] = nil
+ n = n - 1
+ -- If there's nothing left on the stack, and no
+ -- more sides to walk to, we're done and can exit
+ if n == 0 and v == 11 then
+ return
+ end
+ until v < 11
+ -- The next round walk the next neighbor in list.
+ v = v + 1
+ else
+ -- If we did need to walk the neighbor, then
+ -- start walking it from the walk order start (1),
+ -- and not the order we just pushed up the stack.
+ v = 1
+ end
end
end
diff --git a/builtin/game/features.lua b/builtin/game/features.lua
index f082b0db8..2aad458da 100644
--- a/builtin/game/features.lua
+++ b/builtin/game/features.lua
@@ -8,13 +8,14 @@ core.features = {
use_texture_alpha = true,
no_legacy_abms = true,
texture_names_parens = true,
+ area_store_custom_ids = true,
}
function core.has_feature(arg)
if type(arg) == "table" then
- missing_features = {}
- result = true
- for ft, _ in pairs(arg) do
+ local missing_features = {}
+ local result = true
+ for ftr in pairs(arg) do
if not core.features[ftr] then
missing_features[ftr] = true
result = false
diff --git a/builtin/game/init.lua b/builtin/game/init.lua
index 3f82f85c7..a6cfa3bf8 100644
--- a/builtin/game/init.lua
+++ b/builtin/game/init.lua
@@ -1,10 +1,11 @@
-local scriptpath = minetest.get_builtin_path()..DIR_DELIM
+local scriptpath = core.get_builtin_path()..DIR_DELIM
local commonpath = scriptpath.."common"..DIR_DELIM
local gamepath = scriptpath.."game"..DIR_DELIM
dofile(commonpath.."vector.lua")
+dofile(gamepath.."constants.lua")
dofile(gamepath.."item.lua")
dofile(gamepath.."register.lua")
@@ -25,4 +26,3 @@ dofile(gamepath.."features.lua")
dofile(gamepath.."voxelarea.lua")
dofile(gamepath.."forceloading.lua")
dofile(gamepath.."statbars.lua")
-
diff --git a/builtin/game/item.lua b/builtin/game/item.lua
index 6628a4081..36c2c1a68 100644
--- a/builtin/game/item.lua
+++ b/builtin/game/item.lua
@@ -27,19 +27,11 @@ function core.get_pointed_thing_position(pointed_thing, above)
if above then
-- The position where a node would be placed
return pointed_thing.above
- else
- -- The position where a node would be dug
- return pointed_thing.under
end
+ -- The position where a node would be dug
+ return pointed_thing.under
elseif pointed_thing.type == "object" then
- obj = pointed_thing.ref
- if obj ~= nil then
- return obj:getpos()
- else
- return nil
- end
- else
- return nil
+ return pointed_thing.ref and pointed_thing.ref:getpos()
end
end
@@ -96,25 +88,26 @@ function core.dir_to_facedir(dir, is6d)
end
end
+-- Table of possible dirs
+local facedir_to_dir = {
+ {x= 0, y=0, z= 1},
+ {x= 1, y=0, z= 0},
+ {x= 0, y=0, z=-1},
+ {x=-1, y=0, z= 0},
+ {x= 0, y=-1, z= 0},
+ {x= 0, y=1, z= 0},
+}
+-- Mapping from facedir value to index in facedir_to_dir.
+local facedir_to_dir_map = {
+ [0]=1, 2, 3, 4,
+ 5, 2, 6, 4,
+ 6, 2, 5, 4,
+ 1, 5, 3, 6,
+ 1, 6, 3, 5,
+ 1, 4, 3, 2,
+}
function core.facedir_to_dir(facedir)
- --a table of possible dirs
- return ({{x=0, y=0, z=1},
- {x=1, y=0, z=0},
- {x=0, y=0, z=-1},
- {x=-1, y=0, z=0},
- {x=0, y=-1, z=0},
- {x=0, y=1, z=0}})
-
- --indexed into by a table of correlating facedirs
- [({[0]=1, 2, 3, 4,
- 5, 2, 6, 4,
- 6, 2, 5, 4,
- 1, 5, 3, 6,
- 1, 6, 3, 5,
- 1, 4, 3, 2})
-
- --indexed into by the facedir in question
- [facedir]]
+ return facedir_to_dir[facedir_to_dir_map[facedir]]
end
function core.dir_to_wallmounted(dir)
@@ -139,6 +132,19 @@ function core.dir_to_wallmounted(dir)
end
end
+-- table of dirs in wallmounted order
+local wallmounted_to_dir = {
+ [0] = {x = 0, y = 1, z = 0},
+ {x = 0, y = -1, z = 0},
+ {x = 1, y = 0, z = 0},
+ {x = -1, y = 0, z = 0},
+ {x = 0, y = 0, z = 1},
+ {x = 0, y = 0, z = -1},
+}
+function core.wallmounted_to_dir(wallmounted)
+ return wallmounted_to_dir[wallmounted]
+end
+
function core.get_node_drops(nodename, toolname)
local drop = ItemStack({name=nodename}):get_definition().drop
if drop == nil then
@@ -227,7 +233,8 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
place_to = {x = under.x, y = under.y, z = under.z}
end
- if core.is_protected(place_to, placer:get_player_name()) then
+ if core.is_protected(place_to, placer:get_player_name()) and
+ not minetest.check_player_privs(placer, "protection_bypass") then
core.log("action", placer:get_player_name()
.. " tried to place " .. def.name
.. " at protected position "
@@ -334,8 +341,12 @@ function core.item_place(itemstack, placer, pointed_thing, param2)
return itemstack
end
+function core.item_secondary_use(itemstack, placer)
+ return itemstack
+end
+
function core.item_drop(itemstack, dropper, pos)
- if dropper.is_player then
+ if dropper and dropper:is_player() then
local v = dropper:get_look_dir()
local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
local cs = itemstack:get_count()
@@ -349,12 +360,17 @@ function core.item_drop(itemstack, dropper, pos)
v.y = v.y*2 + 2
v.z = v.z*2
obj:setvelocity(v)
+ obj:get_luaentity().dropped_by = dropper:get_player_name()
+ return itemstack
end
else
- core.add_item(pos, itemstack)
+ if core.add_item(pos, itemstack) then
+ return itemstack
+ end
end
- return itemstack
+ -- If we reach this, adding the object to the
+ -- environment failed
end
function core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
@@ -429,7 +445,8 @@ function core.node_dig(pos, node, digger)
return
end
- if core.is_protected(pos, digger:get_player_name()) then
+ if core.is_protected(pos, digger:get_player_name()) and
+ not minetest.check_player_privs(digger, "protection_bypass") then
core.log("action", digger:get_player_name()
.. " tried to dig " .. node.name
.. " at protected position "
@@ -560,6 +577,7 @@ core.nodedef_default = {
diggable = true,
climbable = false,
buildable_to = false,
+ floodable = false,
liquidtype = "none",
liquid_alternative_flowing = "",
liquid_alternative_source = "",
@@ -587,6 +605,7 @@ core.craftitemdef_default = {
-- Interaction callbacks
on_place = redef_wrapper(core, 'item_place'), -- core.item_place
on_drop = redef_wrapper(core, 'item_drop'), -- core.item_drop
+ on_secondary_use = redef_wrapper(core, 'item_secondary_use'),
on_use = nil,
}
@@ -604,6 +623,7 @@ core.tooldef_default = {
-- Interaction callbacks
on_place = redef_wrapper(core, 'item_place'), -- core.item_place
+ on_secondary_use = redef_wrapper(core, 'item_secondary_use'),
on_drop = redef_wrapper(core, 'item_drop'), -- core.item_drop
on_use = nil,
}
@@ -622,6 +642,7 @@ core.noneitemdef_default = { -- This is used for the hand and unknown items
-- Interaction callbacks
on_place = redef_wrapper(core, 'item_place'),
+ on_secondary_use = redef_wrapper(core, 'item_secondary_use'),
on_drop = nil,
on_use = nil,
}
diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua
index 6425a10aa..a66bf33d0 100644
--- a/builtin/game/item_entity.lua
+++ b/builtin/game/item_entity.lua
@@ -4,11 +4,14 @@ function core.spawn_item(pos, item)
-- Take item in any format
local stack = ItemStack(item)
local obj = core.add_entity(pos, "__builtin:item")
- obj:get_luaentity():set_item(stack:to_string())
+ -- Don't use obj if it couldn't be added to the map.
+ if obj then
+ obj:get_luaentity():set_item(stack:to_string())
+ end
return obj
end
--- If item_entity_ttl is not set, enity will have default life time
+-- If item_entity_ttl is not set, enity will have default life time
-- Setting it to -1 disables the feature
local time_to_live = tonumber(core.setting_get("item_entity_ttl"))
@@ -28,6 +31,7 @@ core.register_entity(":__builtin:item", {
spritediv = {x = 1, y = 1},
initial_sprite_basepos = {x = 0, y = 0},
is_visible = false,
+ infotext = "",
},
itemstring = '',
@@ -47,6 +51,7 @@ core.register_entity(":__builtin:item", {
local c = s
local itemtable = stack:to_table()
local itemname = nil
+ local description = ""
if itemtable then
itemname = stack:to_table().name
end
@@ -55,6 +60,7 @@ core.register_entity(":__builtin:item", {
if core.registered_items[itemname] then
item_texture = core.registered_items[itemname].inventory_image
item_type = core.registered_items[itemname].type
+ description = core.registered_items[itemname].description
end
local prop = {
is_visible = true,
@@ -63,6 +69,7 @@ core.register_entity(":__builtin:item", {
visual_size = {x = s, y = s},
collisionbox = {-c, -c, -c, c, c, c},
automatic_rotate = math.pi * 0.5,
+ infotext = description,
}
self.object:set_properties(prop)
end,
@@ -71,7 +78,8 @@ core.register_entity(":__builtin:item", {
return core.serialize({
itemstring = self.itemstring,
always_collect = self.always_collect,
- age = self.age
+ age = self.age,
+ dropped_by = self.dropped_by
})
end,
@@ -81,11 +89,12 @@ core.register_entity(":__builtin:item", {
if data and type(data) == "table" then
self.itemstring = data.itemstring
self.always_collect = data.always_collect
- if data.age then
+ if data.age then
self.age = data.age + dtime_s
else
self.age = dtime_s
end
+ self.dropped_by = data.dropped_by
end
else
self.itemstring = staticdata
@@ -197,9 +206,10 @@ core.register_entity(":__builtin:item", {
end,
on_punch = function(self, hitter)
- if self.itemstring ~= '' then
- local left = hitter:get_inventory():add_item("main", self.itemstring)
- if not left:is_empty() then
+ local inv = hitter:get_inventory()
+ if inv and self.itemstring ~= '' then
+ local left = inv:add_item("main", self.itemstring)
+ if left and not left:is_empty() then
self.itemstring = left:to_string()
return
end
diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua
index e3b7d82bc..de41cfc91 100644
--- a/builtin/game/misc.lua
+++ b/builtin/game/misc.lua
@@ -4,88 +4,81 @@
-- Misc. API functions
--
-local timers = {}
-local mintime
-local function update_timers(delay)
- mintime = false
- local sub = 0
- for index = 1, #timers do
- index = index - sub
- local timer = timers[index]
- timer.time = timer.time - delay
- if timer.time <= 0 then
- core.set_last_run_mod(timer.mod_origin)
- timer.func(unpack(timer.args or {}))
- table.remove(timers, index)
- sub = sub + 1
- elseif mintime then
- mintime = math.min(mintime, timer.time)
- else
- mintime = timer.time
- end
- end
-end
+local jobs = {}
+local time = 0.0
+local last = core.get_us_time() / 1000000
-local timers_to_add
-local function add_timers()
- for _, timer in ipairs(timers_to_add) do
- table.insert(timers, timer)
+core.register_globalstep(function(dtime)
+ local new = core.get_us_time() / 1000000
+ if new > last then
+ time = time + (new - last)
+ else
+ -- Overflow, we may lose a little bit of time here but
+ -- only 1 tick max, potentially running timers slightly
+ -- too early.
+ time = time + new
end
- timers_to_add = false
-end
+ last = new
-local delay = 0
-core.register_globalstep(function(dtime)
- if not mintime then
- -- abort if no timers are running
+ if #jobs < 1 then
return
end
- if timers_to_add then
- add_timers()
- end
- delay = delay + dtime
- if delay < mintime then
- return
+
+ -- Iterate backwards so that we miss any new timers added by
+ -- a timer callback, and so that we don't skip the next timer
+ -- in the list if we remove one.
+ for i = #jobs, 1, -1 do
+ local job = jobs[i]
+ if time >= job.expire then
+ core.set_last_run_mod(job.mod_origin)
+ job.func(unpack(job.arg))
+ table.remove(jobs, i)
+ end
end
- update_timers(delay)
- delay = 0
end)
-function core.after(time, func, ...)
+function core.after(after, func, ...)
assert(tonumber(time) and type(func) == "function",
"Invalid core.after invocation")
- if not mintime then
- mintime = time
- timers_to_add = {{
- time = time+delay,
- func = func,
- args = {...},
- mod_origin = core.get_last_run_mod(),
- }}
- return
- end
- mintime = math.min(mintime, time)
- timers_to_add = timers_to_add or {}
- timers_to_add[#timers_to_add+1] = {
- time = time+delay,
- func = func,
- args = {...},
- mod_origin = core.get_last_run_mod(),
+ jobs[#jobs + 1] = {
+ func = func,
+ expire = time + after,
+ arg = {...},
+ mod_origin = core.get_last_run_mod()
}
end
-function core.check_player_privs(name, privs)
+function core.check_player_privs(player_or_name, ...)
+ local name = player_or_name
+ -- Check if we have been provided with a Player object.
+ if type(name) ~= "string" then
+ name = name:get_player_name()
+ end
+
+ local requested_privs = {...}
local player_privs = core.get_player_privs(name)
local missing_privileges = {}
- for priv, val in pairs(privs) do
- if val
- and not player_privs[priv] then
- table.insert(missing_privileges, priv)
+
+ if type(requested_privs[1]) == "table" then
+ -- We were provided with a table like { privA = true, privB = true }.
+ for priv, value in pairs(requested_privs[1]) do
+ if value and not player_privs[priv] then
+ missing_privileges[#missing_privileges + 1] = priv
+ end
+ end
+ else
+ -- Only a list, we can process it directly.
+ for key, priv in pairs(requested_privs) do
+ if not player_privs[priv] then
+ missing_privileges[#missing_privileges + 1] = priv
+ end
end
end
+
if #missing_privileges > 0 then
return false, missing_privileges
end
+
return true, ""
end
@@ -103,12 +96,31 @@ function core.get_connected_players()
local temp_table = {}
for index, value in pairs(player_list) do
if value:is_player_connected() then
- table.insert(temp_table, value)
+ temp_table[#temp_table + 1] = value
end
end
return temp_table
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
+ return nil
+ end
+
+ local p1 = player:getpos()
+ local p2 = p1
+
+ if radius then
+ p1 = vector.subtract(p1, radius)
+ p2 = vector.add(p2, radius)
+ end
+
+ return p1, p2
+end
+
function core.hash_node_position(pos)
return (pos.z+32768)*65536*65536 + (pos.y+32768)*65536 + pos.x+32768
end
@@ -166,3 +178,22 @@ function core.raillike_group(name)
end
return id
end
+
+-- HTTP callback interface
+function core.http_add_fetch(httpenv)
+ httpenv.fetch = function(req, callback)
+ local handle = httpenv.fetch_async(req)
+
+ local function update_http_status()
+ local res = httpenv.fetch_async_get(handle)
+ if res.completed then
+ callback(res)
+ else
+ core.after(0, update_http_status)
+ end
+ end
+ core.after(0, update_http_status)
+ end
+
+ return httpenv
+end
diff --git a/builtin/game/privileges.lua b/builtin/game/privileges.lua
index 7e6387c72..bd5ead624 100644
--- a/builtin/game/privileges.lua
+++ b/builtin/game/privileges.lua
@@ -32,6 +32,7 @@ core.register_privilege("settime", "Can use /time")
core.register_privilege("privs", "Can modify privileges")
core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges")
core.register_privilege("server", "Can do server maintenance stuff")
+core.register_privilege("protection_bypass", "Can bypass node protection in the world")
core.register_privilege("shout", "Can speak in chat")
core.register_privilege("ban", "Can ban and unban players")
core.register_privilege("kick", "Can kick players")
diff --git a/builtin/game/register.lua b/builtin/game/register.lua
index d0e04bfc3..f330491a2 100644
--- a/builtin/game/register.lua
+++ b/builtin/game/register.lua
@@ -11,10 +11,11 @@ local register_alias_raw = core.register_alias_raw
core.register_alias_raw = nil
--
--- Item / entity / ABM registration functions
+-- Item / entity / ABM / LBM registration functions
--
core.registered_abms = {}
+core.registered_lbms = {}
core.registered_entities = {}
core.registered_items = {}
core.registered_nodes = {}
@@ -51,27 +52,38 @@ local forbidden_item_names = {
local function check_modname_prefix(name)
if name:sub(1,1) == ":" then
- -- Escape the modname prefix enforcement mechanism
+ -- If the name starts with a colon, we can skip the modname prefix
+ -- mechanism.
return name:sub(2)
else
- -- Modname prefix enforcement
+ -- Enforce that the name starts with the correct mod name.
local expected_prefix = core.get_current_modname() .. ":"
if name:sub(1, #expected_prefix) ~= expected_prefix then
error("Name " .. name .. " does not follow naming conventions: " ..
- "\"modname:\" or \":\" prefix required")
+ "\"" .. expected_prefix .. "\" or \":\" prefix required")
end
+
+ -- Enforce that the name only contains letters, numbers and underscores.
local subname = name:sub(#expected_prefix+1)
- if subname:find("[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_]") then
+ if subname:find("[^%w_]") then
error("Name " .. name .. " does not follow naming conventions: " ..
"contains unallowed characters")
end
+
return name
end
end
function core.register_abm(spec)
-- Add to core.registered_abms
- core.registered_abms[#core.registered_abms+1] = spec
+ core.registered_abms[#core.registered_abms + 1] = spec
+ spec.mod_origin = core.get_current_modname() or "??"
+end
+
+function core.register_lbm(spec)
+ -- Add to core.registered_lbms
+ check_modname_prefix(spec.name)
+ core.registered_lbms[#core.registered_lbms + 1] = spec
spec.mod_origin = core.get_current_modname() or "??"
end
@@ -221,7 +233,7 @@ function core.register_alias(name, convert_to)
error("Unable to register alias: Name is forbidden: " .. name)
end
if core.registered_items[name] ~= nil then
- core.log("WARNING: Not registering alias, item with same name" ..
+ core.log("warning", "Not registering alias, item with same name" ..
" is already defined: " .. name .. " -> " .. convert_to)
else
--core.log("Registering alias: " .. name .. " -> " .. convert_to)
@@ -268,6 +280,7 @@ core.register_item(":unknown", {
description = "Unknown Item",
inventory_image = "unknown_item.png",
on_place = core.item_place,
+ on_secondary_use = core.item_secondary_use,
on_drop = core.item_drop,
groups = {not_in_creative_inventory=1},
diggable = true,
@@ -284,6 +297,7 @@ core.register_node(":air", {
pointable = false,
diggable = false,
buildable_to = true,
+ floodable = true,
air_equivalent = true,
drop = "",
groups = {not_in_creative_inventory=1},
@@ -385,7 +399,7 @@ end
local function make_registration()
local t = {}
local registerfunc = function(func)
- table.insert(t, func)
+ t[#t + 1] = func
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
name = debug.getinfo(1, "n").name or "??"
@@ -461,9 +475,9 @@ end
function core.register_on_player_hpchange(func, modifier)
if modifier then
- table.insert(core.registered_on_player_hpchanges.modifiers, func)
+ core.registered_on_player_hpchanges.modifiers[#core.registered_on_player_hpchanges.modifiers + 1] = func
else
- table.insert(core.registered_on_player_hpchanges.loggers, func)
+ core.registered_on_player_hpchanges.loggers[#core.registered_on_player_hpchanges.loggers + 1] = func
end
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
diff --git a/builtin/game/static_spawn.lua b/builtin/game/static_spawn.lua
index 492ab6ca6..100334226 100644
--- a/builtin/game/static_spawn.lua
+++ b/builtin/game/static_spawn.lua
@@ -3,31 +3,23 @@
local function warn_invalid_static_spawnpoint()
if core.setting_get("static_spawnpoint") and
not core.setting_get_pos("static_spawnpoint") then
- core.log('error', "The static_spawnpoint setting is invalid: \""..
+ core.log("error", "The static_spawnpoint setting is invalid: \""..
core.setting_get("static_spawnpoint").."\"")
end
end
warn_invalid_static_spawnpoint()
-local function put_player_in_spawn(obj)
- warn_invalid_static_spawnpoint()
+local function put_player_in_spawn(player_obj)
local static_spawnpoint = core.setting_get_pos("static_spawnpoint")
if not static_spawnpoint then
return false
end
- core.log('action', "Moving "..obj:get_player_name()..
- " to static spawnpoint at "..
- core.pos_to_string(static_spawnpoint))
- obj:setpos(static_spawnpoint)
+ core.log("action", "Moving " .. player_obj:get_player_name() ..
+ " to static spawnpoint at " .. core.pos_to_string(static_spawnpoint))
+ player_obj:setpos(static_spawnpoint)
return true
end
-core.register_on_newplayer(function(obj)
- put_player_in_spawn(obj)
-end)
-
-core.register_on_respawnplayer(function(obj)
- return put_player_in_spawn(obj)
-end)
-
+core.register_on_newplayer(put_player_in_spawn)
+core.register_on_respawnplayer(put_player_in_spawn)