Commit fff67d86 authored by Markus Koschany's avatar Markus Koschany

New upstream version 0.4.17+repack

parent d938c80e
......@@ -13,7 +13,7 @@ set(PROJECT_NAME_CAPITALIZED "Minetest")
# Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing
set(VERSION_MAJOR 0)
set(VERSION_MINOR 4)
set(VERSION_PATCH 16)
set(VERSION_PATCH 17)
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
# Change to false for releases
......
......@@ -14,7 +14,7 @@ android {
buildToolsVersion "25.0.3"
defaultConfig {
versionCode 17
versionCode 18
versionName "${System.env.VERSION_STR}.${versionCode}"
minSdkVersion 9
targetSdkVersion 9
......
......@@ -120,7 +120,12 @@ end
-- The dumped and level arguments are internal-only.
function dump(o, indent, nested, level)
if type(o) ~= "table" then
local t = type(o)
if not level and t == "userdata" then
-- when userdata (e.g. player) is passed directly, print its metatable:
return "userdata metatable: " .. dump(getmetatable(o))
end
if t ~= "table" then
return basic_dump(o)
end
-- Contains table -> true/nil of currently nested tables
......@@ -308,59 +313,25 @@ function core.formspec_escape(text)
end
function core.wrap_text(text, charlimit)
local retval = {}
local current_idx = 1
local start,stop = string_find(text, " ", current_idx)
local nl_start,nl_stop = string_find(text, "\n", current_idx)
local gotnewline = false
if nl_start ~= nil and (start == nil or nl_start < start) then
start = nl_start
stop = nl_stop
gotnewline = true
function core.wrap_text(text, max_length, as_table)
local result = {}
local line = {}
if #text <= max_length then
return as_table and {text} or text
end
local last_line = ""
while start ~= nil do
if string.len(last_line) + (stop-start) > charlimit then
retval[#retval + 1] = last_line
last_line = ""
end
if last_line ~= "" then
last_line = last_line .. " "
end
last_line = last_line .. string_sub(text, current_idx, stop - 1)
if gotnewline then
retval[#retval + 1] = last_line
last_line = ""
gotnewline = false
end
current_idx = stop+1
start,stop = string_find(text, " ", current_idx)
nl_start,nl_stop = string_find(text, "\n", current_idx)
if nl_start ~= nil and (start == nil or nl_start < start) then
start = nl_start
stop = nl_stop
gotnewline = true
for word in text:gmatch('%S+') do
local cur_length = #table.concat(line, ' ')
if cur_length > 0 and cur_length + #word + 1 >= max_length then
-- word wouldn't fit on current line, move to next line
table.insert(result, table.concat(line, ' '))
line = {}
end
table.insert(line, word)
end
--add last part of text
if string.len(last_line) + (string.len(text) - current_idx) > charlimit then
retval[#retval + 1] = last_line
retval[#retval + 1] = string_sub(text, current_idx)
else
last_line = last_line .. " " .. string_sub(text, current_idx)
retval[#retval + 1] = last_line
end
return retval
table.insert(result, table.concat(line, ' '))
return as_table and result or table.concat(result, '\n')
end
--------------------------------------------------------------------------------
......@@ -370,7 +341,7 @@ if INIT == "game" then
local dirs2 = {20, 23, 22, 21}
function core.rotate_and_place(itemstack, placer, pointed_thing,
infinitestacks, orient_flags)
infinitestacks, orient_flags, prevent_after_place)
orient_flags = orient_flags or {}
local unode = core.get_node_or_nil(pointed_thing.under)
......@@ -379,41 +350,20 @@ if INIT == "game" then
end
local undef = core.registered_nodes[unode.name]
if undef and undef.on_rightclick then
undef.on_rightclick(pointed_thing.under, unode, placer,
return undef.on_rightclick(pointed_thing.under, unode, placer,
itemstack, pointed_thing)
return
end
local fdir = core.dir_to_facedir(placer:get_look_dir())
local wield_name = itemstack:get_name()
local fdir = placer and core.dir_to_facedir(placer:get_look_dir()) or 0
local above = pointed_thing.above
local under = pointed_thing.under
local iswall = (above.y == under.y)
local isceiling = not iswall and (above.y < under.y)
local anode = core.get_node_or_nil(above)
if not anode then
return
end
local pos = pointed_thing.above
local node = anode
if undef and undef.buildable_to then
pos = pointed_thing.under
node = unode
iswall = false
end
if core.is_protected(pos, placer:get_player_name()) then
core.record_protection_violation(pos,
placer:get_player_name())
return
end
local ndef = core.registered_nodes[node.name]
if not ndef or not ndef.buildable_to then
return
end
if orient_flags.force_floor then
iswall = false
isceiling = false
......@@ -427,31 +377,26 @@ if INIT == "game" then
iswall = not iswall
end
local param2 = fdir
if iswall then
core.set_node(pos, {name = wield_name,
param2 = dirs1[fdir + 1]})
param2 = dirs1[fdir + 1]
elseif isceiling then
if orient_flags.force_facedir then
core.set_node(pos, {name = wield_name,
param2 = 20})
cparam2 = 20
else
core.set_node(pos, {name = wield_name,
param2 = dirs2[fdir + 1]})
param2 = dirs2[fdir + 1]
end
else -- place right side up
if orient_flags.force_facedir then
core.set_node(pos, {name = wield_name,
param2 = 0})
else
core.set_node(pos, {name = wield_name,
param2 = fdir})
param2 = 0
end
end
if not infinitestacks then
itemstack:take_item()
return itemstack
end
local old_itemstack = ItemStack(itemstack)
local new_itemstack, removed = core.item_place_node(
itemstack, placer, pointed_thing, param2, prevent_after_place
)
return infinitestacks and old_itemstack or new_itemstack
end
......@@ -459,12 +404,18 @@ if INIT == "game" then
--Wrapper for rotate_and_place() to check for sneak and assume Creative mode
--implies infinite stacks when performing a 6d rotation.
--------------------------------------------------------------------------------
local creative_mode_cache = core.settings:get_bool("creative_mode")
local function is_creative(name)
return creative_mode_cache or
core.check_player_privs(name, {creative = true})
end
core.rotate_node = function(itemstack, placer, pointed_thing)
local name = placer and placer:get_player_name() or ""
local invert_wall = placer and placer:get_player_control().sneak or false
core.rotate_and_place(itemstack, placer, pointed_thing,
core.settings:get_bool("creative_mode"),
{invert_wall = placer:get_player_control().sneak})
is_creative(name),
{invert_wall = invert_wall}, true)
return itemstack
end
end
......@@ -642,44 +593,26 @@ end
local ESCAPE_CHAR = string.char(0x1b)
-- Client-side mods don't have access to settings
if core.settings and core.settings:get_bool("disable_escape_sequences") then
function core.get_color_escape_sequence(color)
return ""
end
function core.get_background_escape_sequence(color)
return ""
end
function core.colorize(color, message)
return message
end
else
function core.get_color_escape_sequence(color)
return ESCAPE_CHAR .. "(c@" .. color .. ")"
end
function core.get_background_escape_sequence(color)
return ESCAPE_CHAR .. "(b@" .. color .. ")"
end
function core.get_color_escape_sequence(color)
return ESCAPE_CHAR .. "(c@" .. color .. ")"
end
function core.colorize(color, message)
local lines = tostring(message):split("\n", true)
local color_code = core.get_color_escape_sequence(color)
function core.get_background_escape_sequence(color)
return ESCAPE_CHAR .. "(b@" .. color .. ")"
end
for i, line in ipairs(lines) do
lines[i] = color_code .. line
end
function core.colorize(color, message)
local lines = tostring(message):split("\n", true)
local color_code = core.get_color_escape_sequence(color)
return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff")
for i, line in ipairs(lines) do
lines[i] = color_code .. line
end
return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff")
end
function core.strip_foreground_colors(str)
return (str:gsub(ESCAPE_CHAR .. "%(c@[^)]+%)", ""))
end
......
......@@ -67,16 +67,15 @@ local function save_auth_file()
assert(type(stuff.privileges) == "table")
assert(stuff.last_login == nil or type(stuff.last_login) == "number")
end
local file, errmsg = io.open(core.auth_file_path, 'w+b')
if not file then
error(core.auth_file_path.." could not be opened for writing: "..errmsg)
end
local content = {}
for name, stuff in pairs(core.auth_table) do
local priv_string = core.privs_to_string(stuff.privileges)
local parts = {name, stuff.password, priv_string, stuff.last_login or ""}
file:write(table.concat(parts, ":").."\n")
content[#content + 1] = table.concat(parts, ":")
end
if not core.safe_file_write(core.auth_file_path, table.concat(content, "\n")) then
error(core.auth_file_path.." could not be written to")
end
io.close(file)
end
read_auth_file()
......
......@@ -653,8 +653,8 @@ core.register_chatcommand("pulverize", {
core.rollback_punch_callbacks = {}
core.register_on_punchnode(function(pos, node, puncher)
local name = puncher:get_player_name()
if core.rollback_punch_callbacks[name] then
local name = puncher and puncher:get_player_name()
if name and core.rollback_punch_callbacks[name] then
core.rollback_punch_callbacks[name](pos, node, puncher)
core.rollback_punch_callbacks[name] = nil
end
......@@ -814,7 +814,7 @@ core.register_chatcommand("shutdown", {
message = message or ""
if delay ~= "" then
delay = tonumber(param) or 0
delay = tonumber(delay) or 0
else
delay = 0
core.log("action", name .. " shuts down server")
......
......@@ -60,8 +60,13 @@ core.register_entity(":__builtin:falling_node", {
local pos = self.object:getpos()
-- Position of bottom center point
local bcp = {x = pos.x, y = pos.y - 0.7, z = pos.z}
-- Avoid bugs caused by an unloaded node below
-- 'bcn' is nil for unloaded nodes
local bcn = core.get_node_or_nil(bcp)
-- Delete on contact with ignore at world edges
if bcn and bcn.name == "ignore" then
self.object:remove()
return
end
local bcd = bcn and core.registered_nodes[bcn.name]
if bcn and
(not bcd or bcd.walkable or
......@@ -93,7 +98,7 @@ core.register_entity(":__builtin:falling_node", {
core.remove_node(np)
if nd and nd.buildable_to == false then
-- Add dropped items
local drops = core.get_node_drops(n2.name, "")
local drops = core.get_node_drops(n2, "")
for _, dropped_item in pairs(drops) do
core.add_item(np, dropped_item)
end
......@@ -145,9 +150,9 @@ function core.spawn_falling_node(pos)
end
local function drop_attached_node(p)
local nn = core.get_node(p).name
local n = core.get_node(p)
core.remove_node(p)
for _, item in pairs(core.get_node_drops(nn, "")) do
for _, item in pairs(core.get_node_drops(n, "")) do
local pos = {
x = p.x + math.random()/2 - 0.25,
y = p.y + math.random()/2 - 0.25,
......
......@@ -155,11 +155,45 @@ function core.yaw_to_dir(yaw)
return {x = -math.sin(yaw), y = 0, z = math.cos(yaw)}
end
function core.get_node_drops(nodename, toolname)
function core.is_colored_paramtype(ptype)
return (ptype == "color") or (ptype == "colorfacedir") or
(ptype == "colorwallmounted")
end
function core.strip_param2_color(param2, paramtype2)
if not core.is_colored_paramtype(paramtype2) then
return nil
end
if paramtype2 == "colorfacedir" then
param2 = math.floor(param2 / 32) * 32
elseif paramtype2 == "colorwallmounted" then
param2 = math.floor(param2 / 8) * 8
end
-- paramtype2 == "color" requires no modification.
return param2
end
function core.get_node_drops(node, toolname)
-- Compatibility, if node is string
local nodename = node
local param2 = 0
-- New format, if node is table
if (type(node) == "table") then
nodename = node.name
param2 = node.param2
end
local def = core.registered_nodes[nodename]
local drop = def and def.drop
local ptype = def and def.paramtype2
-- get color, if there is color (otherwise nil)
local palette_index = core.strip_param2_color(param2, ptype)
if drop == nil then
-- default drop
if palette_index then
local stack = ItemStack(nodename)
stack:get_meta():set_int("palette_index", palette_index)
return {stack:to_string()}
end
return {nodename}
elseif type(drop) == "string" then
-- itemstring drop
......@@ -181,6 +215,8 @@ function core.get_node_drops(nodename, toolname)
end
if item.tools ~= nil then
good_tool = false
end
if item.tools ~= nil and toolname then
for _, tool in ipairs(item.tools) do
if tool:sub(1, 1) == '~' then
good_tool = toolname:find(tool:sub(2)) ~= nil
......@@ -191,10 +227,16 @@ function core.get_node_drops(nodename, toolname)
break
end
end
end
end
if good_rarity and good_tool then
got_count = got_count + 1
for _, add_item in ipairs(item.items) do
-- add color, if necessary
if item.inherit_color and palette_index then
local stack = ItemStack(add_item)
stack:get_meta():set_int("palette_index", palette_index)
add_item = stack:to_string()
end
got_items[#got_items+1] = add_item
end
if drop.max_items ~= nil and got_count == drop.max_items then
......@@ -205,7 +247,22 @@ function core.get_node_drops(nodename, toolname)
return got_items
end
function core.item_place_node(itemstack, placer, pointed_thing, param2)
local function user_name(user)
return user and user:get_player_name() or ""
end
local function is_protected(pos, name)
return core.is_protected(pos, name) and
not minetest.check_player_privs(name, "protection_bypass")
end
-- Returns a logging function. For empty names, does not log.
local function make_log(name)
return name ~= "" and core.log or function() end
end
function core.item_place_node(itemstack, placer, pointed_thing, param2,
prevent_after_place)
local def = itemstack:get_definition()
if def.type ~= "node" or pointed_thing.type ~= "node" then
return itemstack, false
......@@ -215,10 +272,11 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
local oldnode_under = core.get_node_or_nil(under)
local above = pointed_thing.above
local oldnode_above = core.get_node_or_nil(above)
local playername = placer:get_player_name()
local playername = user_name(placer)
local log = make_log(playername)
if not oldnode_under or not oldnode_above then
core.log("info", playername .. " tried to place"
log("info", playername .. " tried to place"
.. " node in unloaded position " .. core.pos_to_string(above))
return itemstack, false
end
......@@ -229,7 +287,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
olddef_above = olddef_above or core.nodedef_default
if not olddef_above.buildable_to and not olddef_under.buildable_to then
core.log("info", playername .. " tried to place"
log("info", playername .. " tried to place"
.. " node in invalid position " .. core.pos_to_string(above)
.. ", replacing " .. oldnode_above.name)
return itemstack, false
......@@ -240,13 +298,12 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
-- If node under is buildable_to, place into it instead (eg. snow)
if olddef_under.buildable_to then
core.log("info", "node under is buildable to")
log("info", "node under is buildable to")
place_to = {x = under.x, y = under.y, z = under.z}
end
if core.is_protected(place_to, playername) and
not minetest.check_player_privs(placer, "protection_bypass") then
core.log("action", playername
if is_protected(place_to, playername) then
log("action", playername
.. " tried to place " .. def.name
.. " at protected position "
.. core.pos_to_string(place_to))
......@@ -254,11 +311,11 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
return itemstack
end
core.log("action", playername .. " places node "
log("action", playername .. " places node "
.. def.name .. " at " .. core.pos_to_string(place_to))
local oldnode = core.get_node(place_to)
local newnode = {name = def.name, param1 = 0, param2 = param2}
local newnode = {name = def.name, param1 = 0, param2 = param2 or 0}
-- Calculate direction for wall mounted stuff like torches and signs
if def.place_param2 ~= nil then
......@@ -274,7 +331,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
-- Calculate the direction for furnaces and chests and stuff
elseif (def.paramtype2 == "facedir" or
def.paramtype2 == "colorfacedir") and not param2 then
local placer_pos = placer:getpos()
local placer_pos = placer and placer:getpos()
if placer_pos then
local dir = {
x = above.x - placer_pos.x,
......@@ -282,14 +339,33 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
z = above.z - placer_pos.z
}
newnode.param2 = core.dir_to_facedir(dir)
core.log("action", "facedir: " .. newnode.param2)
log("action", "facedir: " .. newnode.param2)
end
end
local metatable = itemstack:get_meta():to_table().fields
-- Transfer color information
if metatable.palette_index and not def.place_param2 then
local color_divisor = nil
if def.paramtype2 == "color" then
color_divisor = 1
elseif def.paramtype2 == "colorwallmounted" then
color_divisor = 8
elseif def.paramtype2 == "colorfacedir" then
color_divisor = 32
end
if color_divisor then
local color = math.floor(metatable.palette_index / color_divisor)
local other = newnode.param2 % color_divisor
newnode.param2 = color * color_divisor + other
end
end
-- Check if the node is attached and if it can be placed there
if core.get_item_group(def.name, "attached_node") ~= 0 and
not builtin_shared.check_attached_node(place_to, newnode) then
core.log("action", "attached node " .. def.name ..
log("action", "attached node " .. def.name ..
" can not be placed at " .. core.pos_to_string(place_to))
return itemstack, false
end
......@@ -300,7 +376,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
local take_item = true
-- Run callback
if def.after_place_node then
if def.after_place_node and not prevent_after_place then
-- Deepcopy place_to and pointed_thing because callback can modify it
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
......@@ -360,28 +436,27 @@ function core.item_secondary_use(itemstack, placer)
end
function core.item_drop(itemstack, dropper, pos)
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()
local dropper_is_player = dropper and dropper:is_player()
local p = table.copy(pos)
local cnt = itemstack:get_count()
if dropper_is_player then
p.y = p.y + 1.2
if dropper:get_player_control().sneak then
cs = 1
cnt = 1
end
local item = itemstack:take_item(cs)
local obj = core.add_item(p, item)
if obj then
v.x = v.x*2
v.y = v.y*2 + 2
v.z = v.z*2
obj:setvelocity(v)
end
local item = itemstack:take_item(cnt)
local obj = core.add_item(p, item)
if obj then
if dropper_is_player then
local dir = dropper:get_look_dir()
dir.x = dir.x * 2.9
dir.y = dir.y * 2.9 + 2
dir.z = dir.z * 2.9
obj:set_velocity(dir)
obj:get_luaentity().dropped_by = dropper:get_player_name()
return itemstack
end
else
if core.add_item(pos, itemstack) then
return itemstack
end
return itemstack
end
-- If we reach this, adding the object to the
-- environment failed
......@@ -402,7 +477,8 @@ function core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed
itemstack:add_item(replace_with_item)
else
local inv = user:get_inventory()
if inv:room_for_item("main", {name=replace_with_item}) then
-- Check if inv is null, since non-players don't have one
if inv and inv:room_for_item("main", {name=replace_with_item}) then
inv:add_item("main", replace_with_item)
else
local pos = user:getpos()
......@@ -417,7 +493,9 @@ end
function core.item_eat(hp_change, replace_with_item)
return function(itemstack, user, pointed_thing) -- closure
return core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
if user then
return core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
end
end
end
......@@ -434,63 +512,76 @@ end
function core.handle_node_drops(pos, drops, digger)
-- Add dropped items to object's inventory
if digger:get_inventory() then
local _, dropped_item
for _, dropped_item in ipairs(drops) do
local left = digger:get_inventory():add_item("main", dropped_item)
if not left:is_empty() then
local p = {
x = pos.x + math.random()/2-0.25,
y = pos.y + math.random()/2-0.25,
z = pos.z + math.random()/2-0.25,
}
core.add_item(p, left)
end
local inv = digger and digger:get_inventory()
local give_item
if inv then
give_item = function(item)
return inv:add_item("main", item)
end
else
give_item = function(item)
-- itemstring to ItemStack for left:is_empty()
return ItemStack(item)
end
end
for _, dropped_item in pairs(drops) do
local left = give_item(dropped_item)
if not left:is_empty() then
local p = {