summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/common/misc_helpers.lua1
-rw-r--r--builtin/common/strict.lua3
-rw-r--r--builtin/common/vector.lua9
-rw-r--r--builtin/game/auth.lua16
-rw-r--r--builtin/game/chatcommands.lua124
-rw-r--r--builtin/game/constants.lua10
-rw-r--r--builtin/game/detached_inventory.lua4
-rw-r--r--builtin/game/falling.lua110
-rw-r--r--builtin/game/forceloading.lua41
-rw-r--r--builtin/game/init.lua14
-rw-r--r--builtin/game/item.lua11
-rw-r--r--builtin/game/item_entity.lua4
-rw-r--r--builtin/game/misc.lua64
-rw-r--r--builtin/game/mod_profiling.lua356
-rw-r--r--builtin/game/privileges.lua64
-rw-r--r--builtin/game/register.lua43
-rw-r--r--builtin/game/voxelarea.lua49
-rw-r--r--builtin/mainmenu/common.lua10
-rw-r--r--builtin/mainmenu/dlg_config_world.lua48
-rw-r--r--builtin/mainmenu/dlg_settings_advanced.lua108
-rw-r--r--builtin/mainmenu/generate_from_settingtypes.lua99
-rw-r--r--builtin/mainmenu/modmgr.lua18
-rw-r--r--builtin/mainmenu/tab_credits.lua17
-rw-r--r--builtin/mainmenu/tab_mods.lua24
-rw-r--r--builtin/mainmenu/tab_texturepacks.lua6
-rw-r--r--builtin/profiler/init.lua72
-rw-r--r--builtin/profiler/instrumentation.lua232
-rw-r--r--builtin/profiler/reporter.lua277
-rw-r--r--builtin/profiler/sampling.lua206
-rw-r--r--builtin/settingtypes.txt197
30 files changed, 1536 insertions, 701 deletions
diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua
index e4653d41d..c2dc7514d 100644
--- a/builtin/common/misc_helpers.lua
+++ b/builtin/common/misc_helpers.lua
@@ -382,7 +382,6 @@ if INIT == "game" then
itemstack, pointed_thing)
return
end
- local pitch = placer:get_look_pitch()
local fdir = core.dir_to_facedir(placer:get_look_dir())
local wield_name = itemstack:get_name()
diff --git a/builtin/common/strict.lua b/builtin/common/strict.lua
index 05ceadf7a..23ba3d727 100644
--- a/builtin/common/strict.lua
+++ b/builtin/common/strict.lua
@@ -5,6 +5,9 @@ local WARN_INIT = false
function core.global_exists(name)
+ if type(name) ~= "string" then
+ error("core.global_exists: " .. tostring(name) .. " is not a string")
+ end
return rawget(_G, name) ~= nil
end
diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua
index e9ed3aab3..90ba3cc8b 100644
--- a/builtin/common/vector.lua
+++ b/builtin/common/vector.lua
@@ -31,6 +31,14 @@ function vector.normalize(v)
end
end
+function vector.floor(v)
+ return {
+ x = math.floor(v.x),
+ y = math.floor(v.y),
+ z = math.floor(v.z)
+ }
+end
+
function vector.round(v)
return {
x = math.floor(v.x + 0.5),
@@ -130,4 +138,3 @@ function vector.divide(a, b)
z = a.z / b}
end
end
-
diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua
index deb811b14..46fe3d342 100644
--- a/builtin/game/auth.lua
+++ b/builtin/game/auth.lua
@@ -199,3 +199,19 @@ core.register_on_joinplayer(function(player)
record_login(player:get_player_name())
end)
+core.register_on_prejoinplayer(function(name, ip)
+ local auth = core.auth_table
+ if auth[name] ~= nil then
+ return
+ end
+
+ local name_lower = name:lower()
+ for k in pairs(auth) do
+ if k:lower() == name_lower then
+ return string.format("\nCannot create new player called '%s'. "..
+ "Another account called '%s' is already registered. "..
+ "Please check the spelling if it's your account "..
+ "or use a different nickname.", name, k)
+ end
+ end
+end)
diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua
index 3350140ee..2bd93855b 100644
--- a/builtin/game/chatcommands.lua
+++ b/builtin/game/chatcommands.lua
@@ -14,19 +14,6 @@ function core.register_chatcommand(cmd, def)
core.chatcommands[cmd] = def
end
-if core.setting_getbool("mod_profiling") then
- local tracefct = profiling_print_log
- profiling_print_log = nil
- core.register_chatcommand("save_mod_profile",
- {
- params = "",
- description = "save mod profiling data to logfile " ..
- "(depends on default loglevel)",
- func = tracefct,
- privs = { server=true }
- })
-end
-
core.register_on_chat_message(function(name, message)
local cmd, param = string.match(message, "^/([^ ]+) *(.*)")
if not param then
@@ -51,6 +38,12 @@ core.register_on_chat_message(function(name, message)
return true -- Handled chat message
end)
+if core.setting_getbool("profiler.load") then
+ -- Run after register_chatcommand and its register_on_chat_message
+ -- Before any chattcommands that should be profiled
+ profiler.init_chatcommand()
+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)
@@ -102,7 +95,7 @@ core.register_chatcommand("help", {
description = "Get help for commands or list privileges",
func = function(name, param)
local function format_help_line(cmd, def)
- local msg = "/"..cmd
+ local msg = core.colorize("#00ffff", "/"..cmd)
if def.params and def.params ~= "" then
msg = msg .. " " .. def.params
end
@@ -154,60 +147,79 @@ core.register_chatcommand("help", {
core.register_chatcommand("privs", {
params = "<name>",
description = "print out privileges of player",
- func = function(name, param)
- param = (param ~= "" and param or name)
- return true, "Privileges of " .. param .. ": "
+ func = function(caller, param)
+ param = param:trim()
+ local name = (param ~= "" and param or caller)
+ return true, "Privileges of " .. name .. ": "
.. core.privs_to_string(
- core.get_player_privs(param), ' ')
+ core.get_player_privs(name), ' ')
end,
})
+
+local function handle_grant_command(caller, grantname, grantprivstr)
+ local caller_privs = minetest.get_player_privs(caller)
+ if not (caller_privs.privs or caller_privs.basic_privs) then
+ return false, "Your privileges are insufficient."
+ end
+
+ if not core.auth_table[grantname] then
+ return false, "Player " .. grantname .. " does not exist."
+ end
+ local grantprivs = core.string_to_privs(grantprivstr)
+ if grantprivstr == "all" then
+ grantprivs = core.registered_privileges
+ 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 not basic_privs[priv] and not caller_privs.privs then
+ return false, "Your privileges are insufficient."
+ end
+ if not core.registered_privileges[priv] then
+ privs_unknown = privs_unknown .. "Unknown privilege: " .. priv .. "\n"
+ end
+ privs[priv] = true
+ end
+ if privs_unknown ~= "" then
+ return false, privs_unknown
+ end
+ core.set_player_privs(grantname, privs)
+ core.log("action", caller..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
+ if grantname ~= caller then
+ core.chat_send_player(grantname, caller
+ .. " granted you privileges: "
+ .. core.privs_to_string(grantprivs, ' '))
+ end
+ return true, "Privileges of " .. grantname .. ": "
+ .. core.privs_to_string(
+ core.get_player_privs(grantname), ' ')
+end
+
core.register_chatcommand("grant", {
params = "<name> <privilege>|all",
description = "Give privilege to player",
func = function(name, param)
- if not core.check_player_privs(name, {privs=true}) and
- not core.check_player_privs(name, {basic_privs=true}) then
- return false, "Your privileges are insufficient."
- end
local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
if not grantname or not grantprivstr then
return false, "Invalid parameters (see /help grant)"
- elseif not core.auth_table[grantname] then
- return false, "Player " .. grantname .. " does not exist."
- end
- local grantprivs = core.string_to_privs(grantprivstr)
- if grantprivstr == "all" then
- grantprivs = core.registered_privileges
- 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 not basic_privs[priv] and
- not core.check_player_privs(name, {privs=true}) then
- return false, "Your privileges are insufficient."
- end
- if not core.registered_privileges[priv] then
- privs_unknown = privs_unknown .. "Unknown privilege: " .. priv .. "\n"
- end
- privs[priv] = true
- end
- if privs_unknown ~= "" then
- return false, privs_unknown
- end
- core.set_player_privs(grantname, privs)
- core.log("action", name..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
- if grantname ~= name then
- core.chat_send_player(grantname, name
- .. " granted you privileges: "
- .. core.privs_to_string(grantprivs, ' '))
- end
- return true, "Privileges of " .. grantname .. ": "
- .. core.privs_to_string(
- core.get_player_privs(grantname), ' ')
+ end
+ return handle_grant_command(name, grantname, grantprivstr)
+ end,
+})
+
+core.register_chatcommand("grantme", {
+ params = "<privilege>|all",
+ description = "Grant privileges to yourself",
+ func = function(name, param)
+ if param == "" then
+ return false, "Invalid parameters (see /help grantme)"
+ end
+ return handle_grant_command(name, name, param)
end,
})
+
core.register_chatcommand("revoke", {
params = "<name> <privilege>|all",
description = "Remove privilege from player",
diff --git a/builtin/game/constants.lua b/builtin/game/constants.lua
index d0b7c753c..50c515b24 100644
--- a/builtin/game/constants.lua
+++ b/builtin/game/constants.lua
@@ -4,14 +4,24 @@
-- Constants values for use with the Lua API
--
+-- mapnode.h
-- Built-in Content IDs (for use with VoxelManip API)
core.CONTENT_UNKNOWN = 125
core.CONTENT_AIR = 126
core.CONTENT_IGNORE = 127
+-- emerge.h
-- 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
+
+-- constants.h
+-- Size of mapblocks in nodes
+core.MAP_BLOCKSIZE = 16
+
+-- light.h
+-- Maximum value for node 'light_source' parameter
+core.LIGHT_MAX = 14
diff --git a/builtin/game/detached_inventory.lua b/builtin/game/detached_inventory.lua
index b5d106b04..420e89ff2 100644
--- a/builtin/game/detached_inventory.lua
+++ b/builtin/game/detached_inventory.lua
@@ -2,7 +2,7 @@
core.detached_inventories = {}
-function core.create_detached_inventory(name, callbacks)
+function core.create_detached_inventory(name, callbacks, player_name)
local stuff = {}
stuff.name = name
if callbacks then
@@ -15,6 +15,6 @@ function core.create_detached_inventory(name, callbacks)
end
stuff.mod_origin = core.get_current_modname() or "??"
core.detached_inventories[name] = stuff
- return core.create_detached_inventory_raw(name)
+ return core.create_detached_inventory_raw(name, player_name)
end
diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua
index 57bb98cfd..4696ce481 100644
--- a/builtin/game/falling.lua
+++ b/builtin/game/falling.lua
@@ -1,5 +1,7 @@
-- Minetest: builtin/item.lua
+local builtin_shared = ...
+
--
-- Falling stuff
--
@@ -41,19 +43,20 @@ core.register_entity(":__builtin:falling_node", {
end,
on_step = function(self, dtime)
- -- Set gravity
+ -- 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
+ -- Turn to actual node when colliding with ground, or continue to 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 bcn = core.get_node(bcp)
- local bcd = core.registered_nodes[bcn.name]
- -- Note: walkable is in the node definition, not in item groups
- if not bcd or
- (bcd.walkable or
+ -- 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
+ local bcn = core.get_node_or_nil(bcp)
+ local bcd = bcn and core.registered_nodes[bcn.name]
+ if bcn and
+ (not bcd or bcd.walkable or
(core.get_item_group(self.node.name, "float") ~= 0 and
bcd.liquidtype ~= "none")) then
if bcd and bcd.leveled and
@@ -75,20 +78,20 @@ core.register_entity(":__builtin:falling_node", {
local np = {x = bcp.x, y = bcp.y + 1, z = bcp.z}
-- Check what's here
local n2 = core.get_node(np)
+ local nd = core.registered_nodes[n2.name]
-- If it's not air or liquid, remove node and replace it with
-- it's drops
- if n2.name ~= "air" and (not core.registered_nodes[n2.name] or
- core.registered_nodes[n2.name].liquidtype == "none") then
+ if n2.name ~= "air" and (not nd or nd.liquidtype == "none") then
core.remove_node(np)
- if core.registered_nodes[n2.name].buildable_to == false then
+ if nd.buildable_to == false then
-- Add dropped items
local drops = core.get_node_drops(n2.name, "")
- for _, dropped_item in ipairs(drops) do
+ for _, dropped_item in pairs(drops) do
core.add_item(np, dropped_item)
end
end
-- Run script hook
- for _, callback in ipairs(core.registered_on_dignodes) do
+ for _, callback in pairs(core.registered_on_dignodes) do
callback(np, n2)
end
end
@@ -97,7 +100,7 @@ core.register_entity(":__builtin:falling_node", {
core.add_node(np, self.node)
end
self.object:remove()
- nodeupdate(np)
+ core.check_for_falling(np)
return
end
local vel = self.object:getvelocity()
@@ -108,15 +111,17 @@ core.register_entity(":__builtin:falling_node", {
end
})
-function spawn_falling_node(p, node)
+local function spawn_falling_node(p, node)
local obj = core.add_entity(p, "__builtin:falling_node")
- obj:get_luaentity():set_node(node)
+ if obj then
+ obj:get_luaentity():set_node(node)
+ end
end
-function drop_attached_node(p)
+local 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 pairs(core.get_node_drops(nn, "")) do
local pos = {
x = p.x + math.random()/2 - 0.25,
y = p.y + math.random()/2 - 0.25,
@@ -126,11 +131,15 @@ function drop_attached_node(p)
end
end
-function check_attached_node(p, n)
+function builtin_shared.check_attached_node(p, n)
local def = core.registered_nodes[n.name]
local d = {x = 0, y = 0, z = 0}
if def.paramtype2 == "wallmounted" then
- d = core.wallmounted_to_dir(n.param2)
+ -- The fallback vector here is in case 'wallmounted to dir' is nil due
+ -- to voxelmanip placing a wallmounted node without resetting a
+ -- pre-existing param2 value that is out-of-range for wallmounted.
+ -- The fallback vector corresponds to param2 = 0.
+ d = core.wallmounted_to_dir(n.param2) or {x = 0, y = 1, z = 0}
else
d.y = -1
end
@@ -147,19 +156,23 @@ end
-- Some common functions
--
-function nodeupdate_single(p)
+function core.check_single_for_falling(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 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
+ -- Only spawn falling node if node below is loaded
+ local n_bottom = core.get_node_or_nil(p_bottom)
+ local d_bottom = n_bottom and core.registered_nodes[n_bottom.name]
+ if d_bottom and
+
(core.get_item_group(n.name, "float") == 0 or
- core.registered_nodes[n_bottom.name].liquidtype == "none") and
- (n.name ~= n_bottom.name or (core.registered_nodes[n_bottom.name].leveled and
- 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
+ d_bottom.liquidtype == "none") and
+
+ (n.name ~= n_bottom.name or (d_bottom.leveled and
+ core.get_node_level(p_bottom) <
+ core.get_node_max_level(p_bottom))) and
+
+ (not d_bottom.walkable or d_bottom.buildable_to) then
n.level = core.get_node_level(p)
core.remove_node(p)
spawn_falling_node(p, n)
@@ -168,7 +181,7 @@ function nodeupdate_single(p)
end
if core.get_item_group(n.name, "attached_node") ~= 0 then
- if not check_attached_node(p, n) then
+ if not builtin_shared.check_attached_node(p, n) then
drop_attached_node(p)
return true
end
@@ -181,7 +194,7 @@ end
-- 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 = {
+local check_for_falling_neighbors = {
{x = -1, y = -1, z = 0},
{x = 1, y = -1, z = 0},
{x = 0, y = -1, z = -1},
@@ -195,7 +208,7 @@ local nodeupdate_neighbors = {
{x = 0, y = 1, z = 0},
}
-function nodeupdate(p)
+function core.check_for_falling(p)
-- Round p to prevent falling entities to get stuck.
p = vector.round(p)
@@ -214,10 +227,10 @@ function nodeupdate(p)
n = n + 1
s[n] = {p = p, v = v}
-- Select next node from neighbor list.
- p = vector.add(p, nodeupdate_neighbors[v])
+ p = vector.add(p, check_for_falling_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 not core.check_single_for_falling(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
@@ -249,12 +262,33 @@ end
-- Global callbacks
--
-function on_placenode(p, node)
- nodeupdate(p)
+local function on_placenode(p, node)
+ core.check_for_falling(p)
end
core.register_on_placenode(on_placenode)
-function on_dignode(p, node)
- nodeupdate(p)
+local function on_dignode(p, node)
+ core.check_for_falling(p)
end
core.register_on_dignode(on_dignode)
+
+local function on_punchnode(p, node)
+ core.check_for_falling(p)
+end
+core.register_on_punchnode(on_punchnode)
+
+--
+-- Globally exported functions
+--
+
+-- TODO remove this function after the 0.4.15 release
+function nodeupdate(p)
+ core.log("deprecated", "nodeupdate: deprecated, please use core.check_for_falling instead")
+ core.check_for_falling(p)
+end
+
+-- TODO remove this function after the 0.4.15 release
+function nodeupdate_single(p)
+ core.log("deprecated", "nodeupdate_single: deprecated, please use core.check_single_for_falling instead")
+ core.check_single_for_falling(p)
+end
diff --git a/builtin/game/forceloading.lua b/builtin/game/forceloading.lua
index 8c9fbf512..8a05de36c 100644
--- a/builtin/game/forceloading.lua
+++ b/builtin/game/forceloading.lua
@@ -5,9 +5,10 @@ core.forceload_block = nil
core.forceload_free_block = nil
local blocks_forceloaded
+local blocks_temploaded = {}
local total_forceloaded = 0
-local BLOCKSIZE = 16
+local BLOCKSIZE = core.MAP_BLOCKSIZE
local function get_blockpos(pos)
return {
x = math.floor(pos.x/BLOCKSIZE),
@@ -15,32 +16,52 @@ local function get_blockpos(pos)
z = math.floor(pos.z/BLOCKSIZE)}
end
-function core.forceload_block(pos)
+-- When we create/free a forceload, it's either transient or persistent. We want
+-- to add to/remove from the table that corresponds to the type of forceload, but
+-- we also need the other table because whether we forceload a block depends on
+-- both tables.
+-- This function returns the "primary" table we are adding to/removing from, and
+-- the other table.
+local function get_relevant_tables(transient)
+ if transient then
+ return blocks_temploaded, blocks_forceloaded
+ else
+ return blocks_forceloaded, blocks_temploaded
+ end
+end
+
+function core.forceload_block(pos, transient)
local blockpos = get_blockpos(pos)
local hash = core.hash_node_position(blockpos)
- if blocks_forceloaded[hash] ~= nil then
- blocks_forceloaded[hash] = blocks_forceloaded[hash] + 1
+ local relevant_table, other_table = get_relevant_tables(transient)
+ if relevant_table[hash] ~= nil then
+ relevant_table[hash] = relevant_table[hash] + 1
return true
+ elseif other_table[hash] ~= nil then
+ relevant_table[hash] = 1
else
if total_forceloaded >= (tonumber(core.setting_get("max_forceloaded_blocks")) or 16) then
return false
end
total_forceloaded = total_forceloaded+1
- blocks_forceloaded[hash] = 1
+ relevant_table[hash] = 1
forceload_block(blockpos)
return true
end
end
-function core.forceload_free_block(pos)
+function core.forceload_free_block(pos, transient)
local blockpos = get_blockpos(pos)
local hash = core.hash_node_position(blockpos)
- if blocks_forceloaded[hash] == nil then return end
- if blocks_forceloaded[hash] > 1 then
- blocks_forceloaded[hash] = blocks_forceloaded[hash] - 1
+ local relevant_table, other_table = get_relevant_tables(transient)
+ if relevant_table[hash] == nil then return end
+ if relevant_table[hash] > 1 then
+ relevant_table[hash] = relevant_table[hash] - 1
+ elseif other_table[hash] ~= nil then
+ relevant_table[hash] = nil
else
total_forceloaded = total_forceloaded-1
- blocks_forceloaded[hash] = nil
+ relevant_table[hash] = nil
forceload_free_block(blockpos)
end
end
diff --git a/builtin/game/init.lua b/builtin/game/init.lua
index a6cfa3bf8..b5e2f7cca 100644
--- a/builtin/game/init.lua
+++ b/builtin/game/init.lua
@@ -3,14 +3,18 @@ local scriptpath = core.get_builtin_path()..DIR_DELIM
local commonpath = scriptpath.."common"..DIR_DELIM
local gamepath = scriptpath.."game"..DIR_DELIM
+-- Shared between builtin files, but
+-- not exposed to outer context
+local builtin_shared = {}
+
dofile(commonpath.."vector.lua")
dofile(gamepath.."constants.lua")
-dofile(gamepath.."item.lua")
+assert(loadfile(gamepath.."item.lua"))(builtin_shared)
dofile(gamepath.."register.lua")
-if core.setting_getbool("mod_profiling") then
- dofile(gamepath.."mod_profiling.lua")
+if core.setting_getbool("profiler.load") then
+ profiler = dofile(scriptpath.."profiler"..DIR_DELIM.."init.lua")
end
dofile(gamepath.."item_entity.lua")
@@ -21,8 +25,10 @@ dofile(gamepath.."auth.lua")
dofile(gamepath.."chatcommands.lua")
dofile(gamepath.."static_spawn.lua")
dofile(gamepath.."detached_inventory.lua")
-dofile(gamepath.."falling.lua")
+assert(loadfile(gamepath.."falling.lua"))(builtin_shared)
dofile(gamepath.."features.lua")
dofile(gamepath.."voxelarea.lua")
dofile(gamepath.."forceloading.lua")
dofile(gamepath.."statbars.lua")
+
+profiler = nil
diff --git a/builtin/game/item.lua b/builtin/game/item.lua
index 36c2c1a68..bf456a4e0 100644
--- a/builtin/game/item.lua
+++ b/builtin/game/item.lua
@@ -1,5 +1,7 @@
-- Minetest: builtin/item.lua
+local builtin_shared = ...
+
local function copy_pointed_thing(pointed_thing)
return {
type = pointed_thing.type,
@@ -250,7 +252,9 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
local newnode = {name = def.name, param1 = 0, param2 = param2}
-- Calculate direction for wall mounted stuff like torches and signs
- if def.paramtype2 == 'wallmounted' and not param2 then
+ if def.place_param2 ~= nil then
+ newnode.param2 = def.place_param2
+ elseif def.paramtype2 == 'wallmounted' and not param2 then
local dir = {
x = under.x - above.x,
y = under.y - above.y,
@@ -273,7 +277,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
-- 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 check_attached_node(place_to, newnode) then
+ not builtin_shared.check_attached_node(place_to, newnode) then
core.log("action", "attached node " .. def.name ..
" can not be placed at " .. core.pos_to_string(place_to))
return itemstack, false
@@ -470,6 +474,9 @@ function core.node_dig(pos, node, digger)
-- Wear out tool
if not core.setting_getbool("creative_mode") then
wielded:add_wear(dp.wear)
+ if wielded:get_count() == 0 and wdef.sound and wdef.sound.breaks then
+ core.sound_play(wdef.sound.breaks, {pos = pos, gain = 0.5})
+ end
end
end
digger:set_wielded_item(wielded)
diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua
index a66bf33d0..be158c119 100644
--- a/builtin/game/item_entity.lua
+++ b/builtin/game/item_entity.lua
@@ -31,7 +31,6 @@ core.register_entity(":__builtin:item", {
spritediv = {x = 1, y = 1},
initial_sprite_basepos = {x = 0, y = 0},
is_visible = false,
- infotext = "",
},
itemstring = '',
@@ -51,7 +50,6 @@ 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
@@ -60,7 +58,6 @@ 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,
@@ -69,7 +66,6 @@ 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,
diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua
index de41cfc91..7caa9e7ba 100644
--- a/builtin/game/misc.lua
+++ b/builtin/game/misc.lua
@@ -38,7 +38,7 @@ core.register_globalstep(function(dtime)
end)
function core.after(after, func, ...)
- assert(tonumber(time) and type(func) == "function",
+ assert(tonumber(after) and type(func) == "function",
"Invalid core.after invocation")
jobs[#jobs + 1] = {
func = func,
@@ -48,11 +48,13 @@ function core.after(after, func, ...)
}
end
-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
+function core.check_player_privs(name, ...)
+ local arg_type = type(name)
+ if (arg_type == "userdata" or arg_type == "table") and
+ name.get_player_name then -- If it quacks like a Player...
name = name:get_player_name()
+ elseif arg_type ~= "string" then
+ error("Invalid core.check_player_privs argument type: " .. arg_type, 2)
end
local requested_privs = {...}
@@ -85,11 +87,21 @@ end
local player_list = {}
core.register_on_joinplayer(function(player)
- player_list[player:get_player_name()] = player
+ local player_name = player:get_player_name()
+ player_list[player_name] = player
+ if not minetest.is_singleplayer() then
+ core.chat_send_all("*** " .. player_name .. " joined the game.")
+ end
end)
-core.register_on_leaveplayer(function(player)
- player_list[player:get_player_name()] = nil
+core.register_on_leaveplayer(function(player, timed_out)
+ local player_name = player:get_player_name()
+ player_list[player_name] = nil
+ local announcement = "*** " .. player_name .. " left the game."
+ if timed_out then
+ announcement = announcement .. " (timed out)"
+ end
+ core.chat_send_all(announcement)
end)
function core.get_connected_players()
@@ -197,3 +209,39 @@ function core.http_add_fetch(httpenv)
return httpenv
end
+
+if minetest.setting_getbool("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
+
+ local ESCAPE_CHAR = string.char(0x1b)
+ 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.colorize(color, message)
+ return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("#ffffff")
+ end
+
+end
+
+function core.close_formspec(player_name, formname)
+ return minetest.show_formspec(player_name, formname, "")
+end
+
diff --git a/builtin/game/mod_profiling.lua b/builtin/game/mod_profiling.lua
deleted file mode 100644
index df2d10221..000000000
--- a/builtin/game/mod_profiling.lua
+++ /dev/null
@@ -1,356 +0,0 @@
--- Minetest: builtin/game/mod_profiling.lua
-
-local mod_statistics = {}
-mod_statistics.step_total = 0
-mod_statistics.data = {}
-mod_statistics.stats = {}
-mod_statistics.stats["total"] = {
- min_us = math.huge,
- max_us = 0,
- avg_us = 0,
- min_per = 100,
- max_per = 100,
- avg_per = 100
-}
-
-local replacement_table = {
- "register_globalstep",
- "register_on_placenode",
- "register_on_dignode",
- "register_on_punchnode",
- "register_on_generated",
- "register_on_newplayer",
- "register_on_dieplayer",
- "register_on_respawnplayer",
- "register_on_prejoinplayer",
- "register_on_joinplayer",
- "register_on_leaveplayer",
- "register_on_cheat",
- "register_on_chat_message",
- "register_on_player_receive_fields",
- "register_on_mapgen_init",
- "register_on_craft",
- "register_craft_predict",
- "register_on_item_eat"
-}
-
---------------------------------------------------------------------------------
-function mod_statistics.log_time(type, modname, time_in_us)
-
- if modname == nil then
- modname = "builtin"
- end
-
- if mod_statistics.data[modname] == nil then
- mod_statistics.data[modname] = {}
- end
-
- if mod_statistics.data[modname][type] == nil then
- mod_statistics.data[modname][type] = 0
- end
-
- mod_statistics.data[modname][type] =
- mod_statistics.data[modname][type] + time_in_us
- mod_statistics.step_total = mod_statistics.step_total + time_in_us
-end
-
---------------------------------------------------------------------------------
-function mod_statistics.update_statistics(dtime)
- for modname,types in pairs(mod_statistics.data) do
-
- if mod_statistics.stats[modname] == nil then
- mod_statistics.stats[modname] = {
- min_us = math.huge,
- max_us = 0,
- avg_us = 0,
- min_per = 100,
- max_per = 0,
- avg_per = 0
- }
- end
-
- local modtime = 0
- for type,time in pairs(types) do
- modtime = modtime + time
- if mod_statistics.stats[modname].types == nil then
- mod_statistics.stats[modname].types = {}
- end
- if mod_statistics.stats[modname].types[type] == nil then
- mod_statistics.stats[modname].types[type] = {
- min_us = math.huge,
- max_us = 0,
- avg_us = 0,
- min_per = 100,
- max_per = 0,
- avg_per = 0
- }
- end
-
- local toupdate = mod_statistics.stats[modname].types[type]
-
- --update us values
- toupdate.min_us = math.min(time, toupdate.min_us)
- toupdate.max_us = math.max(time, toupdate.max_us)
- --TODO find better algorithm
- toupdate.avg_us = toupdate.avg_us * 0.99 + modtime * 0.01
-
- --update percentage values
- local cur_per = (time/mod_statistics.step_total) * 100
- toupdate.min_per = math.min(toupdate.min_per, cur_per)
-
- toupdate.max_per = math.max(toupdate.max_per, cur_per)
-
- --TODO find better algorithm
- toupdate.avg_per = toupdate.avg_per * 0.99 + cur_per * 0.01
-
- mod_statistics.data[modname][type] = 0
- end
-
- --per mod statistics
- --update us values
- mod_statistics.stats[modname].min_us =
- math.min(modtime, mod_statistics.stats[modname].min_us)
- mod_statistics.stats[modname].max_us =
- math.max(modtime, mod_statistics.stats[modname].max_us)
- --TODO find better algorithm
- mod_statistics.stats[modname].avg_us =
- mod_statistics.stats[modname].avg_us * 0.99 + modtime * 0.01
-
- --update percentage values
- local cur_per = (modtime/mod_statistics.step_total) * 100
- mod_statistics.stats[modname].min_per =
- math.min(mod_statistics.stats[modname].min_per, cur_per)
-
- mod_statistics.stats[modname].max_per =
- math.max(mod_statistics.stats[modname].max_per, cur_per)
-
- --TODO find better algorithm
- mod_statistics.stats[modname].avg_per =
- mod_statistics.stats[modname].avg_per * 0.99 + cur_per * 0.01
- end
-
- --update "total"
- mod_statistics.stats["total"].min_us =
- math.min(mod_statistics.step_total, mod_statistics.stats["total"].min_us)
- mod_statistics.stats["total"].max_us =
- math.max(mod_statistics.step_total, mod_statistics.stats["total"].max_us)
- --TODO find better algorithm
- mod_statistics.stats["total"].avg_us =
- mod_statistics.stats["total"].avg_us * 0.99 +
- mod_statistics.step_total * 0.01
-
- mod_statistics.step_total = 0
-end
-
---------------------------------------------------------------------------------
-local function build_callback(log_id, fct)
- return function( toregister )
- local modname = core.get_current_modname()
-
- fct(function(...)
- local starttime = core.get_us_time()
- -- note maximum 10 return values are supported unless someone finds
- -- a way to store a variable lenght return value list
- local r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 = toregister(...)
- local delta = core.get_us_time() - starttime
- mod_statistics.log_time(log_id, modname, delta)
- return r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
- end
- )
- end
-end
-
---------------------------------------------------------------------------------
-function profiling_print_log(cmd, filter)
-
- print("Filter:" .. dump(filter))
-
- core.log("action", "Values below show times/percentages per server step.")
- core.log("action", "Following suffixes are used for entities:")
- core.log("action", "\t#oa := on_activate, #os := on_step, #op := on_punch, #or := on_rightclick, #gs := get_staticdata")
- core.log("action",
- string.format("%16s | %25s | %10s | %10s | %10s | %9s | %9s | %9s",
- "modname", "type" , "min µs", "max µs", "avg µs", "min %", "max %", "avg %")
- )
- core.log("action",
- "-----------------+---------------------------+-----------+" ..
- "-----------+-----------+-----------+-----------+-----------")
- for modname,statistics in pairs(mod_statistics.stats) do
- if modname ~= "total" then
-
- if (filter == "") or (modname == filter) then
- if modname:len() > 16 then
- modname = "..." .. modname:sub(-13)
- end
-
- core.log("action",
- string.format("%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d",
- modname,
- " ",
- statistics.min_us,
- statistics.max_us,
- statistics.avg_us,
- statistics.min_per,
- statistics.max_per,
- statistics.avg_per)
- )
- if core.setting_getbool("detailed_profiling") then
- if statistics.types ~= nil then
- for type,typestats in pairs(statistics.types) do
-
- if type:len() > 25 then
- type = "..." .. type:sub(-22)
- end
-
- core.log("action",
- string.format(
- "%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d",
- " ",
- type,
- typestats.min_us,
- typestats.max_us,
- typestats.avg_us,
- typestats.min_per,
- typestats.max_per,
- typestats.avg_per)
- )
- end
- end
- end
- end
- end
- end
- core.log("action",
- "-----------------+---------------------------+-----------+" ..
- "-----------+-----------+-----------+-----------+-----------")
-
- if filter == "" then
- core.log("action",
- string.format("%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d",
- "total",
- " ",
- mod_statistics.stats["total"].min_us,
- mod_statistics.stats["total"].max_us,
- mod_statistics.stats["total"].avg_us,
- mod_statistics.stats["total"].min_per,
- mod_statistics.stats["total"].max_per,
- mod_statistics.stats["total"].avg_per)
- )
- end
- core.log("action", " ")
-
- return true
-end
-
---------------------------------------------------------------------------------
-local function initialize_profiling()
- core.log("action", "Initialize tracing")
-
- mod_statistics.entity_callbacks = {}
-
- -- first register our own globalstep handler
- core.register_globalstep(mod_statistics.update_statistics)
-
- local rp_register_entity = core.register_entity
- core.register_entity = function(name, prototype)
- local modname = core.get_current_modname()
- local new_on_activate = nil
- local new_on_step = nil
- local new_on_punch = nil
- local new_rightclick = nil
- local new_get_staticdata = nil
-
- if prototype.on_activate ~= nil then
- local cbid = name .. "#oa"
- mod_statistics.entity_callbacks[cbid] = prototype.on_activate
- new_on_activate = function(self, staticdata, dtime_s)
- local starttime = core.get_us_time()
- mod_statistics.entity_callbacks[cbid](self, staticdata, dtime_s)
- local delta = core.get_us_time() -starttime
- mod_statistics.log_time(cbid, modname, delta)
- end
- end
-
- if prototype.on_step ~= nil then
- local cbid = name .. "#os"
- mod_statistics.entity_callbacks[cbid] = prototype.on_step
- new_on_step = function(self, dtime)
- local starttime = core.get_us_time()
- mod_statistics.entity_callbacks[cbid](self, dtime)
- local delta = core.get_us_time() -starttime
- mod_statistics.log_time(cbid, modname, delta)
- end
- end
-
- if prototype.on_punch ~= nil then
- local cbid = name .. "#op"
- mod_statistics.entity_callbacks[cbid] = prototype.on_punch
- new_on_punch = function(self, hitter)
- local starttime = core.get_us_time()
- mod_statistics.entity_callbacks[cbid](self, hitter)
- local delta = core.get_us_time() -starttime
- mod_statistics.log_time(cbid, modname, delta)
- end
- end
-
- if prototype.rightclick ~= nil then
- local cbid = name .. "#rc"
- mod_statistics.entity_callbacks[cbid] = prototype.rightclick
- new_rightclick = function(self, clicker)
- local starttime = core.get_us_time()
- mod_statistics.entity_callbacks[cbid](self, clicker)
- local delta = core.get_us_time() -starttime
- mod_statistics.log_time(cbid, modname, delta)
- end
- end
-
- if prototype.get_staticdata ~= nil then
- local cbid = name .. "#gs"
- mod_statistics.entity_callbacks[cbid] = prototype.get_staticdata
- new_get_staticdata = function(self)
- local starttime = core.get_us_time()
- local retval = mod_statistics.entity_callbacks[cbid](self)
- local delta = core.get_us_time() -starttime
- mod_statistics.log_time(cbid, modname, delta)
- return retval
- end
- end
-
- prototype.on_activate = new_on_activate
- prototype.on_step = new_on_step
- prototype.on_punch = new_on_punch
- prototype.rightclick = new_rightclick
- prototype.get_staticdata = new_get_staticdata
-
- rp_register_entity(name,prototype)
- end
-
- for i,v in ipairs(replacement_table) do
- local to_replace = core[v]
- core[v] = build_callback(v, to_replace)
- end
-
- local rp_register_abm = core.register_abm
- core.register_abm = function(spec)
-
- local modname = core.get_current_modname()
-
- local replacement_spec = {
- nodenames = spec.nodenames,
- neighbors = spec.neighbors,
- interval = spec.interval,
- chance = spec.chance,
- action = function(pos, node, active_object_count, active_object_count_wider)
- local starttime = core.get_us_time()
- spec.action(pos, node, active_object_count, active_object_count_wider)
- local delta = core.get_us_time() - starttime
- mod_statistics.log_time("abm", modname, delta)
- end
- }
- rp_register_abm(replacement_spec)
- end
-
- core.log("action", "Mod profiling initialized")
-end
-
-initialize_profiling()
diff --git a/builtin/game/privileges.lua b/builtin/game/privileges.lua
index bd5ead624..56e090f4c 100644
--- a/builtin/game/privileges.lua
+++ b/builtin/game/privileges.lua
@@ -26,18 +26,46 @@ function core.register_privilege(name, param)
end
core.register_privilege("interact", "Can interact with things and modify the world")
-core.register_privilege("teleport", "Can use /teleport command")
-core.register_privilege("bring", "Can teleport other players")
-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")
-core.register_privilege("give", "Can use /give and /giveme")
-core.register_privilege("password", "Can use /setpassword and /clearpassword")
+core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges")
+core.register_privilege("privs", "Can modify privileges")
+
+core.register_privilege("teleport", {
+ description = "Can use /teleport command",
+ give_to_singleplayer = false,
+})
+core.register_privilege("bring", {
+ description = "Can teleport other players",
+ give_to_singleplayer = false,
+})
+core.register_privilege("settime", {
+ description = "Can use /time",
+ give_to_singleplayer = false,
+})
+core.register_privilege("server", {
+ description = "Can do server maintenance stuff",
+ give_to_singleplayer = false,
+})
+core.register_privilege("protection_bypass", {
+ description = "Can bypass node protection in the world",
+ give_to_singleplayer = false,
+})
+core.register_privilege("ban", {
+ description = "Can ban and unban players",
+ give_to_singleplayer = false,
+})
+core.register_privilege("kick", {
+ description = "Can kick players",
+ give_to_singleplayer = false,
+})
+core.register_privilege("give", {
+ description = "Can use /give and /giveme",
+ give_to_singleplayer = false,
+})
+core.register_privilege("password", {
+ description = "Can use /setpassword and /clearpassword",
+ give_to_singleplayer = false,
+})
core.register_privilege("fly", {
description = "Can fly using the free_move mode",
give_to_singleplayer = false,
@@ -50,5 +78,15 @@ core.register_privilege("noclip", {
description = "Can fly through walls",
give_to_singleplayer = false,
})
-core.register_privilege("rollback", "Can use the rollback functionality")
-
+core.register_privilege("rollback", {
+ description = "Can use the rollback functionality",
+ give_to_singleplayer = false,
+})
+core.register_privilege("zoom", {
+ description = "Can zoom the camera",
+ give_to_singleplayer = false,
+})
+core.register_privilege("debug", {
+ description = "Allows enabling various debug options that may affect gameplay",
+ give_to_singleplayer = false,
+})
diff --git a/builtin/game/register.lua b/builtin/game/register.lua
index f330491a2..90f095e9f 100644
--- a/builtin/game/register.lua
+++ b/builtin/game/register.lua
@@ -7,6 +7,9 @@
local register_item_raw = core.register_item_raw
core.register_item_raw = nil
+local unregister_item_raw = core.unregister_item_raw
+core.unregister_item_raw = nil
+
local register_alias_raw = core.register_alias_raw
core.register_alias_raw = nil
@@ -124,6 +127,11 @@ function core.register_item(name, itemdef)
fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8},
}
end
+ if itemdef.light_source and itemdef.light_source > core.LIGHT_MAX then
+ itemdef.light_source = core.LIGHT_MAX
+ core.log("warning", "Node 'light_source' value exceeds maximum," ..
+ " limiting to maximum: " ..name)
+ end
setmetatable(itemdef, {__index = core.nodedef_default})
core.registered_nodes[itemdef.name] = itemdef
elseif itemdef.type == "craft" then
@@ -172,6 +180,27 @@ function core.register_item(name, itemdef)
register_item_raw(itemdef)
end
+function core.unregister_item(name)
+ if not core.registered_items[name] then
+ core.log("warning", "Not unregistering item " ..name..
+ " because it doesn't exist.")
+ return
+ end
+ -- Erase from registered_* table
+ local type = core.registered_items[name].type
+ if type == "node" then
+ core.registered_nodes[name] = nil
+ elseif type == "craft" then
+ core.registered_craftitems[name] = nil
+ elseif type == "tool" then
+ core.registered_tools[name] = nil
+ end
+ core.registered_items[name] = nil
+
+
+ unregister_item_raw(name)
+end
+
function core.register_node(name, nodedef)
nodedef.type = "node"
core.register_item(name, nodedef)
@@ -242,6 +271,20 @@ function core.register_alias(name, convert_to)
end
end
+function core.register_alias_force(name, convert_to)
+ if forbidden_item_names[name] then
+ error("Unable to register alias: Name is forbidden: " .. name)
+ end
+ if core.registered_items[name] ~= nil then
+ core.unregister_item(name)
+ core.log("info", "Removed item " ..name..
+ " while attempting to force add an alias")
+ end
+ --core.log("Registering alias: " .. name .. " -> " .. convert_to)
+ core.registered_aliases[name] = convert_to
+ register_alias_raw(name, convert_to)
+end
+
function core.on_craft(itemstack, player, old_craft_list, craft_inv)
for _, func in ipairs(core.registered_on_crafts) do
itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack
diff --git a/builtin/game/voxelarea.lua b/builtin/game/voxelarea.lua
index 6d926c940..724761414 100644
--- a/builtin/game/voxelarea.lua
+++ b/builtin/game/voxelarea.lua
@@ -50,7 +50,7 @@ end
function VoxelArea:position(i)
local p = {}
local MinEdge = self.MinEdge
-
+
i = i - 1
p.z = math.floor(i / self.zstride) + MinEdge.z
@@ -84,23 +84,46 @@ end
function VoxelArea:iter(minx, miny, minz, maxx, maxy, maxz)
local i = self:index(minx, miny, minz) - 1
- local last = self:index(maxx, maxy, maxz)
- local ystride = self.ystride
- local zstride = self.zstride
- local yoff = (last+1) % ystride
- local zoff = (last+1) % zstride
- local ystridediff = (i - last) % ystride
- local zstridediff = (i - last) % zstride
+ local xrange = maxx - minx + 1
+ local nextaction = i + 1 + xrange
+
+ local y = 0
+ local yrange = maxy - miny + 1
+ local yreqstride = self.ystride - xrange
+
+ local z = 0
+ local zrange = maxz - minz + 1
+ local multistride = self.zstride - ((yrange - 1) * self.ystride + xrange)
+
return function()
+ -- continue i until it needs to jump
i = i + 1
- if i % zstride == zoff then
- i = i + zstridediff
- elseif i % ystride == yoff then
- i = i + ystridediff
+ if i ~= nextaction then
+ return i
end
- if i <= last then
+
+ -- continue y until maxy is exceeded
+ y = y + 1
+ if y ~= yrange then
+ -- set i to index(minx, miny + y, minz + z) - 1
+ i = i + yreqstride
+ nextaction = i + xrange
return i
end
+
+ -- continue z until maxz is exceeded
+ z = z + 1
+ if z == zrange then
+ -- cuboid finished, return nil
+ return
+ end
+
+ -- set i to index(minx, miny, minz + z) - 1
+ i = i + multistride
+
+ y = 0
+ nextaction = i + xrange
+ return i
end
end
diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua
index 1fd89ff77..da3667828 100644
--- a/builtin/mainmenu/common.lua
+++ b/builtin/mainmenu/common.lua
@@ -248,14 +248,18 @@ end
--------------------------------------------------------------------------------
function is_server_protocol_compat(server_proto_min, server_proto_max)
- return min_supp_proto <= (server_proto_max or 24) and max_supp_proto >= (server_proto_min or 13)
+ if (not server_proto_min) or (not server_proto_max) then
+ -- There is no info. Assume the best and act as if we would be compatible.
+ return true
+ end
+ return min_supp_proto <= server_proto_max and max_supp_proto >= server_proto_min
end
--------------------------------------------------------------------------------
function is_server_protocol_compat_or_error(server_proto_min, server_proto_max)
if not is_server_protocol_compat(server_proto_min, server_proto_max) then
local server_prot_ver_info, client_prot_ver_info
- local s_p_min = server_proto_min or 13
- local s_p_max = server_proto_max or 24
+ local s_p_min = server_proto_min
+ local s_p_max = server_proto_max
if s_p_min ~= s_p_max then
server_prot_ver_info = fgettext_ne("Server supports protocol versions between $1 and $2. ",
diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/dlg_config_world.lua
index eb0319ba0..7b3ab9852 100644
--- a/builtin/mainmenu/dlg_config_world.lua
+++ b/builtin/mainmenu/dlg_config_world.lua
@@ -16,6 +16,9 @@
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--------------------------------------------------------------------------------
+
+local enabled_all = false
+
local function modname_valid(name)
return not name:find("[^a-z0-9_]")
end
@@ -44,13 +47,18 @@ local function get_formspec(data)
if mod == nil then
mod = {name=""}
end
+
+ local hard_deps, soft_deps = modmgr.get_dependencies(mod.path)
retval = retval ..
"label[0,0.7;" .. fgettext("Mod:") .. "]" ..
"label[0.75,0.7;" .. mod.name .. "]" ..
- "label[0,1.25;" .. fgettext("Depends:") .. "]" ..
- "textlist[0,1.75;5,4.25;world_config_depends;" ..
- modmgr.get_dependencies(mod.path) .. ";0]" ..
+ "label[0,1.25;" .. fgettext("Dependencies:") .. "]" ..
+ "textlist[0,1.75;5,2.125;world_config_depends;" ..
+ hard_deps .. ";0]" ..
+ "label[0,3.875;" .. fgettext("Optional dependencies:") .. "]" ..
+ "textlist[0,4.375;5,1.8;world_config_optdepends;" ..
+ soft_deps .. ";0]" ..
"button[3.25,7;2.5,0.5;btn_config_world_save;" .. fgettext("Save") .. "]" ..
"button[5.75,7;2.5,0.5;btn_config_world_cancel;" .. fgettext("Cancel") .. "]"
@@ -80,11 +88,15 @@ local function get_formspec(data)
end
end
end
-
- retval = retval ..
- "button[8.75,0.125;2.5,0.5;btn_all_mods;" .. fgettext("Enable all") .. "]" ..
- "textlist[5.5,0.75;5.75,5.25;world_config_modlist;"
-
+ if enabled_all then
+ retval = retval ..
+ "button[8.75,0.125;2.5,0.5;btn_disable_all_mods;" .. fgettext("Disable all") .. "]" ..
+ "textlist[5.5,0.75;5.75,5.4;world_config_modlist;"
+ else
+ retval = retval ..
+ "button[8.75,0.125;2.5,0.5;btn_enable_all_mods;" .. fgettext("Enable all") .. "]" ..
+ "textlist[5.5,0.75;5.75,5.4;world_config_modlist;"
+ end
retval = retval .. modmgr.render_modlist(data.list)
retval = retval .. ";" .. data.selected_mod .."]"
@@ -229,15 +241,27 @@ local function handle_buttons(this, fields)
return true
end
- if fields["btn_all_mods"] then
+ if fields.btn_enable_all_mods then
local list = this.data.list:get_raw_list()
- for i=1,#list,1 do
- if list[i].typ ~= "game_mod" and
- not list[i].is_modpack then
+ for i = 1, #list do
+ if list[i].typ ~= "game_mod" and not list[i].is_modpack then
list[i].enabled = true
end
end
+ enabled_all = true
+ return true
+ end
+
+ if fields.btn_disable_all_mods then
+ local list = this.data.list:get_raw_list()
+
+ for i = 1, #list do
+ if list[i].typ ~= "game_mod" and not list[i].is_modpack then
+ list[i].enabled = false
+ end
+ end
+ enabled_all = false
return true
end
diff --git a/builtin/mainmenu/dlg_settings_advanced.lua b/builtin/mainmenu/dlg_settings_advanced.lua
index f5e80c252..b0d923768 100644
--- a/builtin/mainmenu/dlg_settings_advanced.lua
+++ b/builtin/mainmenu/dlg_settings_advanced.lua
@@ -479,13 +479,13 @@ local function handle_change_setting_buttons(this, fields)
return true
end
if setting.min and new_value < setting.min then
- this.data.error_message = fgettext_ne("The value must be greater than $1.", setting.min)
+ this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
this.data.entered_text = fields["te_setting_value"]
core.update_formspec(this:get_formspec())
return true
end
if setting.max and new_value > setting.max then
- this.data.error_message = fgettext_ne("The value must be lower than $1.", setting.max)
+ this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
this.data.entered_text = fields["te_setting_value"]
core.update_formspec(this:get_formspec())
return true
@@ -659,102 +659,12 @@ function create_adv_settings_dlg()
return dlg
end
-local function create_minetest_conf_example()
- local result = "# This file contains a list of all available settings and their default value for minetest.conf\n" ..
- "\n" ..
- "# By default, all the settings are commented and not functional.\n" ..
- "# Uncomment settings by removing the preceding #.\n" ..
- "\n" ..
- "# minetest.conf is read by default from:\n" ..
- "# ../minetest.conf\n" ..
- "# ../../minetest.conf\n" ..
- "# Any other path can be chosen by passing the path as a parameter\n" ..
- "# to the program, eg. \"minetest.exe --config ../minetest.conf.example\".\n" ..
- "\n" ..
- "# Further documentation:\n" ..
- "# http://wiki.minetest.net/\n" ..
- "\n"
-
- local settings = parse_config_file(true, false)
- for _, entry in ipairs(settings) do
- if entry.type == "category" then
- if entry.level == 0 then
- result = result .. "#\n# " .. entry.name .. "\n#\n\n"
- else
- for i = 1, entry.level do
- result = result .. "#"
- end
- result = result .. "# " .. entry.name .. "\n\n"
- end
- else
- if entry.comment ~= "" then
- for _, comment_line in ipairs(entry.comment:split("\n", true)) do
- result = result .."# " .. comment_line .. "\n"
- end
- end
- result = result .. "# type: " .. entry.type
- if entry.min then
- result = result .. " min: " .. entry.min
- end
- if entry.max then
- result = result .. " max: " .. entry.max
- end
- if entry.values then
- result = result .. " values: " .. table.concat(entry.values, ", ")
- end
- if entry.possible then
- result = result .. " possible values: " .. entry.possible:gsub(",", ", ")
- end
- result = result .. "\n"
- local append = ""
- if entry.default ~= "" then
- append = " " .. entry.default
- end
- result = result .. "# " .. entry.name .. " =" .. append .. "\n\n"
- end
- end
- return result
-end
+-- Generate minetest.conf.example and settings_translation_file.cpp
-local function create_translation_file()
- local result = "// This file is automatically generated\n" ..
- "// It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files\n" ..
- "// To update it, refer to the bottom of builtin/mainmenu/tab_settings.lua\n\n" ..
- "fake_function() {\n"
+-- *** Please note ***
+-- There is text in minetest.conf.example that will not be generated from
+-- settingtypes.txt but must be preserved:
+-- The documentation of mapgen noise parameter formats (title plus 16 lines)
+-- Noise parameter 'mgv5_np_ground' in group format (13 lines)
- local settings = parse_config_file(true, false)
- for _, entry in ipairs(settings) do
- if entry.type == "category" then
- local name_escaped = entry.name:gsub("\"", "\\\"")
- result = result .. "\tgettext(\"" .. name_escaped .. "\");\n"
- else
- if entry.readable_name then
- local readable_name_escaped = entry.readable_name:gsub("\"", "\\\"")
- result = result .. "\tgettext(\"" .. readable_name_escaped .. "\");\n"
- end
- if entry.comment ~= "" then
- local comment_escaped = entry.comment:gsub("\n", "\\n")
- comment_escaped = comment_escaped:gsub("\"", "\\\"")
- result = result .. "\tgettext(\"" .. comment_escaped .. "\");\n"
- end
- end
- end
- result = result .. "}\n"
- return result
-end
-
-if false then
- local file = io.open("minetest.conf.example", "w")
- if file then
- file:write(create_minetest_conf_example())
- file:close()
- end
-end
-
-if false then
- local file = io.open("src/settings_translation_file.cpp", "w")
- if file then
- file:write(create_translation_file())
- file:close()
- end
-end
+--assert(loadfile(core.get_mainmenu_path()..DIR_DELIM.."generate_from_settingtypes.lua"))(parse_config_file(true, false))
diff --git a/builtin/mainmenu/generate_from_settingtypes.lua b/builtin/mainmenu/generate_from_settingtypes.lua
new file mode 100644
index 000000000..6c9ba27fb
--- /dev/null
+++ b/builtin/mainmenu/generate_from_settingtypes.lua
@@ -0,0 +1,99 @@
+local settings = ...
+
+local concat = table.concat
+local insert = table.insert
+local sprintf = string.format
+local rep = string.rep
+
+local minetest_example_header = [[
+# This file contains a list of all available settings and their default value for minetest.conf
+
+# By default, all the settings are commented and not functional.
+# Uncomment settings by removing the preceding #.
+
+# minetest.conf is read by default from:
+# ../minetest.conf
+# ../../minetest.conf
+# Any other path can be chosen by passing the path as a parameter
+# to the program, eg. "minetest.exe --config ../minetest.conf.example".
+
+# Further documentation:
+# http://wiki.minetest.net/
+
+]]
+
+local function create_minetest_conf_example()
+ local result = { minetest_example_header }
+ for _, entry in ipairs(settings) do
+ if entry.type == "category" then
+ if entry.level == 0 then
+ insert(result, "#\n# " .. entry.name .. "\n#\n\n")
+ else
+ insert(result, rep("#", entry.level))
+ insert(result, "# " .. entry.name .. "\n\n")
+ end
+ else
+ if entry.comment ~= "" then
+ for _, comment_line in ipairs(entry.comment:split("\n", true)) do
+ insert(result, "# " .. comment_line .. "\n")
+ end
+ end
+ insert(result, "# type: " .. entry.type)
+ if entry.min then
+ insert(result, " min: " .. entry.min)
+ end
+ if entry.max then
+ insert(result, " max: " .. entry.max)
+ end
+ if entry.values then
+ insert(result, " values: " .. concat(entry.values, ", "))
+ end
+ if entry.possible then
+ insert(result, " possible values: " .. entry.possible:gsub(",", ", "))
+ end
+ insert(result, "\n")
+ local append
+ if entry.default ~= "" then
+ append = " " .. entry.default
+ end
+ insert(result, sprintf("# %s =%s\n\n", entry.name, append or ""))
+ end
+ end
+ return concat(result)
+end
+
+local translation_file_header = [[
+// This file is automatically generated
+// It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files
+// To update it, refer to the bottom of builtin/mainmenu/dlg_settings_advanced.lua
+
+fake_function() {]]
+
+local function create_translation_file()
+ local result = { translation_file_header }
+ for _, entry in ipairs(settings) do
+ if entry.type == "category" then
+ insert(result, sprintf("\tgettext(%q);", entry.name))
+ else
+ if entry.readable_name then
+ insert(result, sprintf("\tgettext(%q);", entry.readable_name))
+ end
+ if entry.comment ~= "" then
+ local comment_escaped = entry.comment:gsub("\n", "\\n")
+ comment_escaped = comment_escaped:gsub("\"", "\\\"")
+ insert(result, "\tgettext(\"" .. comment_escaped .. "\");")
+ end
+ end
+ end
+ insert(result, "}\n")
+ return concat(result, "\n")
+end
+
+local file = assert(io.open("minetest.conf.example", "w"))
+file:write(create_minetest_conf_example())
+file:close()
+
+file = assert(io.open("src/settings_translation_file.cpp", "w"))
+file:write(create_translation_file())
+file:close()
+
diff --git a/builtin/mainmenu/modmgr.lua b/builtin/mainmenu/modmgr.lua
index f996df7ba..2b7b371bf 100644
--- a/builtin/mainmenu/modmgr.lua
+++ b/builtin/mainmenu/modmgr.lua
@@ -284,27 +284,33 @@ end
--------------------------------------------------------------------------------
function modmgr.get_dependencies(modfolder)
- local toadd = ""
+ local toadd_hard = ""
+ local toadd_soft = ""
if modfolder ~= nil then
local filename = modfolder ..
DIR_DELIM .. "depends.txt"
+ local hard_dependencies = {}
+ local soft_dependencies = {}
local dependencyfile = io.open(filename,"r")
-
if dependencyfile then
local dependency = dependencyfile:read("*l")
while dependency do
- if toadd ~= "" then
- toadd = toadd .. ","
+ dependency = dependency:gsub("\r", "")
+ if string.sub(dependency, -1, -1) == "?" then
+ table.insert(soft_dependencies, string.sub(dependency, 1, -2))
+ else
+ table.insert(hard_dependencies, dependency)
end
- toadd = toadd .. dependency
dependency = dependencyfile:read()
end
dependencyfile:close()
end
+ toadd_hard = table.concat(hard_dependencies, ",")
+ toadd_soft = table.concat(soft_dependencies, ",")
end
- return toadd
+ return toadd_hard, toadd_soft
end
--------------------------------------------------------------------------------
diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_credits.lua
index 4d2ffd7f4..c2ad19183 100644
--- a/builtin/mainmenu/tab_credits.lua
+++ b/builtin/mainmenu/tab_credits.lua
@@ -29,20 +29,23 @@ local core_developers = {
"Loic Blot (nerzhul/nrz) <loic.blot@unix-experience.fr>",
"Matt Gregory (paramat)",
"est31 <MTest31@outlook.com>",
- "Craig Robbins (Zeno)",
+ "Craig Robbins (Zeno) <craig.d.robbins@gmail.com>",
+ "Auke Kok (sofar) <sofar@foo-projects.org>",
+ "Andrew Ward (rubenwardy) <rubenwardy@gmail.com>",
}
local active_contributors = {
- "Auke Kok (sofar) <sofar@foo-projects.org>",
"Duane Robertson <duane@duanerobertson.com>",
"SmallJoker <mk939@ymail.com>",
- "Andrew Ward (rubenwardy) <rubenwardy@gmail.com>",
+ "Lars Hofhansl <larsh@apache.org>",
"Jeija <jeija@mesecons.net>",
"Gregory Currie (gregorycu)",
"Sokomine <wegwerf@anarres.dyndns.org>",
"TeTpaAka",
"Jean-Patrick G (kilbith) <jeanpatrick.guerrero@gmail.com>",
"Diego Martínez (kaeza) <kaeza@users.sf.net>",
+ "Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
+ "Rogier <rogier777@gmail.com>",
}
local previous_core_developers = {
@@ -76,17 +79,13 @@ return {
caption = fgettext("Credits"),
cbf_formspec = function(tabview, name, tabdata)
local logofile = defaulttexturedir .. "logo.png"
+ local version = core.get_version()
return "image[0.5,1;" .. core.formspec_escape(logofile) .. "]" ..
- "label[0.5,3.2;Minetest " .. core.get_version() .. "]" ..
+ "label[0.5,3.2;" .. version.project .. " " .. version.string .. "]" ..
"label[0.5,3.5;http://minetest.net]" ..
"tablecolumns[color;text]" ..
"tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
"table[3.5,-0.25;8.5,5.8;list_credits;" ..
- "#FFFF00," .. "Dedication of the current release" .. ",," ..
- "The 0.4.14 release is dedicated to the memory of" .. ",," ..
- "Minetest developer Maciej Kasatkin (RealBadAngel)" .. ",," ..
- "who died on March 24 2016." .. ",," ..
- "Our thoughts are with his family and friends." .. ",,," ..
"#FFFF00," .. fgettext("Core Developers") .. ",," ..
table.concat(core_developers, ",,") .. ",,," ..
"#FFFF00," .. fgettext("Active Contributors") .. ",," ..
diff --git a/builtin/mainmenu/tab_mods.lua b/builtin/mainmenu/tab_mods.lua
index 5b59aa110..4a5b6c041 100644
--- a/builtin/mainmenu/tab_mods.lua
+++ b/builtin/mainmenu/tab_mods.lua
@@ -98,12 +98,24 @@ local function get_formspec(tabview, name, tabdata)
.. fgettext("Uninstall selected modpack") .. "]"
else
--show dependencies
-
- retval = retval .. "," .. fgettext("Depends:") .. ","
-
- local toadd = modmgr.get_dependencies(selected_mod.path)
-
- retval = retval .. toadd .. ";0]"
+ local toadd_hard, toadd_soft = modmgr.get_dependencies(selected_mod.path)
+ if toadd_hard == "" and toadd_soft == "" then
+ retval = retval .. "," .. fgettext("No dependencies.")
+ else
+ if toadd_hard ~= "" then
+ retval = retval .. "," .. fgettext("Dependencies:") .. ","
+ retval = retval .. toadd_hard
+ end
+ if toadd_soft ~= "" then
+ if toadd_hard ~= "" then
+ retval = retval .. ","
+ end
+ retval = retval .. "," .. fgettext("Optional dependencies:") .. ","
+ retval = retval .. toadd_soft
+ end
+ end
+
+ retval = retval .. ";0]"
retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;"
.. fgettext("Uninstall selected mod") .. "]"
diff --git a/builtin/mainmenu/tab_texturepacks.lua b/builtin/mainmenu/tab_texturepacks.lua
index a102fd61d..4638beaa1 100644
--- a/builtin/mainmenu/tab_texturepacks.lua
+++ b/builtin/mainmenu/tab_texturepacks.lua
@@ -73,7 +73,7 @@ local function get_formspec(tabview, name, tabdata)
if not file_exists(infofile) then
infofile = current_texture_path .. DIR_DELIM .. "info.txt"
if file_exists(infofile) then
- core.log("info.txt is depreciated. description.txt should be used instead.")
+ core.log("deprecated", "info.txt is deprecated. description.txt should be used instead.")
end
end
@@ -96,8 +96,8 @@ local function get_formspec(tabview, name, tabdata)
return retval ..
render_texture_pack_list(list) ..
";" .. index .. "]" ..
- "image[0.25,0.25;4.0,3.7;" .. core.formspec_escape(screenfile or no_screenshot) .. "]" ..
- "textarea[0.6,3.5;3.7,1.5;;" .. core.formspec_escape(infotext or "") .. ";]"
+ "image[0.25,0.25;4.05,2.7;" .. core.formspec_escape(screenfile or no_screenshot) .. "]" ..
+ "textarea[0.6,2.85;3.7,1.5;;" .. core.formspec_escape(infotext or "") .. ";]"
end
--------------------------------------------------------------------------------
diff --git a/builtin/profiler/init.lua b/builtin/profiler/init.lua
new file mode 100644
index 000000000..c1597d280
--- /dev/null
+++ b/builtin/profiler/init.lua
@@ -0,0 +1,72 @@
+--Minetest
+--Copyright (C) 2016 T4im
+--
+--This program is free software; you can redistribute it and/or modify
+--it under the terms of the GNU Lesser General Public License as published by
+--the Free Software Foundation; either version 2.1 of the License, or
+--(at your option) any later version.
+--
+--This program is distributed in the hope that it will be useful,
+--but WITHOUT ANY WARRANTY; without even the implied warranty of
+--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+--GNU Lesser General Public License for more details.
+--
+--You should have received a copy of the GNU Lesser General Public License along
+--with this program; if not, write to the Free Software Foundation, Inc.,
+--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+local profiler_path = core.get_builtin_path()..DIR_DELIM.."profiler"..DIR_DELIM
+local profiler = {}
+local sampler = assert(loadfile(profiler_path .. "sampling.lua"))(profiler)
+local instrumentation = assert(loadfile(profiler_path .. "instrumentation.lua"))(profiler, sampler)
+local reporter = dofile(profiler_path .. "reporter.lua")
+profiler.instrument = instrumentation.instrument
+
+---
+-- Delayed registration of the /profiler chat command
+-- Is called later, after `core.register_chatcommand` was set up.
+--
+function profiler.init_chatcommand()
+ local instrument_profiler = core.setting_getbool("instrument.profiler") or false
+ if instrument_profiler then
+ instrumentation.init_chatcommand()
+ end
+
+ local param_usage = "print [filter] | dump [filter] | save [format [filter]] | reset"
+ core.register_chatcommand("profiler", {
+ description = "handle the profiler and profiling data",
+ params = param_usage,
+ privs = { server=true },
+ func = function(name, param)
+ local command, arg0 = string.match(param, "([^ ]+) ?(.*)")
+ local args = arg0 and string.split(arg0, " ")
+
+ if command == "dump" then
+ core.log("action", reporter.print(sampler.profile, arg0))
+ return true, "Statistics written to action log"
+ elseif command == "print" then
+ return true, reporter.print(sampler.profile, arg0)
+ elseif command == "save" then
+ return reporter.save(sampler.profile, args[1] or "txt", args[2])
+ elseif command == "reset" then
+ sampler.reset()
+ return true, "Statistics were reset"
+ end
+
+ return false, string.format(
+ "Usage: %s\n" ..
+ "Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).",
+ param_usage
+ )
+ end
+ })
+
+ if not instrument_profiler then
+ instrumentation.init_chatcommand()
+ end
+end
+
+sampler.init()
+instrumentation.init()
+
+return profiler
diff --git a/builtin/profiler/instrumentation.lua b/builtin/profiler/instrumentation.lua
new file mode 100644
index 000000000..4311215b2
--- /dev/null
+++ b/builtin/profiler/instrumentation.lua
@@ -0,0 +1,232 @@
+--Minetest
+--Copyright (C) 2016 T4im
+--
+--This program is free software; you can redistribute it and/or modify
+--it under the terms of the GNU Lesser General Public License as published by
+--the Free Software Foundation; either version 2.1 of the License, or
+--(at your option) any later version.
+--
+--This program is distributed in the hope that it will be useful,
+--but WITHOUT ANY WARRANTY; without even the implied warranty of
+--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+--GNU Lesser General Public License for more details.
+--
+--You should have received a copy of the GNU Lesser General Public License along
+--with this program; if not, write to the Free Software Foundation, Inc.,
+--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+local format, pairs, type = string.format, pairs, type
+local core, get_current_modname = core, core.get_current_modname
+local profiler, sampler = ...
+local instrument_builtin = core.setting_getbool("instrument.builtin") or false
+
+local register_functions = {
+ register_globalstep = 0,
+ register_playerevent = 0,
+ register_on_placenode = 0,
+ register_on_dignode = 0,
+ register_on_punchnode = 0,
+ register_on_generated = 0,
+ register_on_newplayer = 0,
+ register_on_dieplayer = 0,
+ register_on_respawnplayer = 0,
+ register_on_prejoinplayer = 0,
+ register_on_joinplayer = 0,
+ register_on_leaveplayer = 0,
+ register_on_cheat = 0,
+ register_on_chat_message = 0,
+ register_on_player_receive_fields = 0,
+ register_on_craft = 0,
+ register_craft_predict = 0,
+ register_on_protection_violation = 0,
+ register_on_item_eat = 0,
+ register_on_punchplayer = 0,
+ register_on_player_hpchange = 0,
+}
+
+---
+-- Create an unique instrument name.
+-- Generate a missing label with a running index number.
+--
+local counts = {}
+local function generate_name(def)
+ local class, label, func_name = def.class, def.label, def.func_name
+ if label then
+ if class or func_name then
+ return format("%s '%s' %s", class or "", label, func_name or ""):trim()
+ end
+ return format("%s", label):trim()
+ elseif label == false then
+ return format("%s", class or func_name):trim()
+ end
+
+ local index_id = def.mod .. (class or func_name)
+ local index = counts[index_id] or 1
+ counts[index_id] = index + 1
+ return format("%s[%d] %s", class or func_name, index, class and func_name or ""):trim()
+end
+
+---
+-- Keep `measure` and the closure in `instrument` lean, as these, and their
+-- directly called functions are the overhead that is caused by instrumentation.
+--
+local time, log = core.get_us_time, sampler.log
+local function measure(modname, instrument_name, start, ...)
+ log(modname, instrument_name, time() - start)
+ return ...
+end
+--- Automatically instrument a function to measure and log to the sampler.
+-- def = {
+-- mod = "",
+-- class = "",
+-- func_name = "",
+-- -- if nil, will create a label based on registration order
+-- label = "" | false,
+-- }
+local function instrument(def)
+ if not def or not def.func then
+ return
+ end
+ def.mod = def.mod or get_current_modname()
+ local modname = def.mod
+ local instrument_name = generate_name(def)
+ local func = def.func
+
+ if not instrument_builtin and modname == "*builtin*" then
+ return func
+ end
+
+ return function(...)
+ -- This tail-call allows passing all return values of `func`
+ -- also called https://en.wikipedia.org/wiki/Continuation_passing_style
+ -- Compared to table creation and unpacking it won't lose `nil` returns
+ -- and is expected to be faster
+ -- `measure` will be executed after time() and func(...)
+ return measure(modname, instrument_name, time(), func(...))
+ end
+end
+
+local function can_be_called(func)
+ -- It has to be a function or callable table
+ return type(func) == "function" or
+ ((type(func) == "table" or type(func) == "userdata") and
+ getmetatable(func) and getmetatable(func).__call)
+end
+
+local function assert_can_be_called(func, func_name, level)
+ if not can_be_called(func) then
+ -- Then throw an *helpful* error, by pointing on our caller instead of us.
+ error(format("Invalid argument to %s. Expected function-like type instead of '%s'.", func_name, type(func)), level + 1)
+ end
+end
+
+---
+-- Wraps a registration function `func` in such a way,
+-- that it will automatically instrument any callback function passed as first argument.
+--
+local function instrument_register(func, func_name)
+ local register_name = func_name:gsub("^register_", "", 1)
+ return function(callback, ...)
+ assert_can_be_called(callback, func_name, 2)
+ register_functions[func_name] = register_functions[func_name] + 1
+ return func(instrument {
+ func = callback,
+ func_name = register_name
+ }), ...
+ end
+end
+
+local function init_chatcommand()
+ if core.setting_getbool("instrument.chatcommand") or true then
+ local orig_register_chatcommand = core.register_chatcommand
+ core.register_chatcommand = function(cmd, def)
+ def.func = instrument {
+ func = def.func,
+ label = "/" .. cmd,
+ }
+ orig_register_chatcommand(cmd, def)
+ end
+ end
+end
+
+---
+-- Start instrumenting selected functions
+--
+local function init()
+ local is_set = core.setting_getbool
+ if is_set("instrument.entity") or true then
+ -- Explicitly declare entity api-methods.
+ -- Simple iteration would ignore lookup via __index.
+ local entity_instrumentation = {
+ "on_activate",
+ "on_step",
+ "on_punch",
+ "rightclick",
+ "get_staticdata",
+ }
+ -- Wrap register_entity() to instrument them on registration.
+ local orig_register_entity = core.register_entity
+ core.register_entity = function(name, prototype)
+ local modname = get_current_modname()
+ for _, func_name in pairs(entity_instrumentation) do
+ prototype[func_name] = instrument {
+ func = prototype[func_name],
+ mod = modname,
+ func_name = func_name,
+ label = prototype.label,
+ }
+ end
+ orig_register_entity(name,prototype)
+ end
+ end
+
+ if is_set("instrument.abm") or true then
+ -- Wrap register_abm() to automatically instrument abms.
+ local orig_register_abm = core.register_abm
+ core.register_abm = function(spec)
+ spec.action = instrument {
+ func = spec.action,
+ class = "ABM",
+ label = spec.label,
+ }
+ orig_register_abm(spec)
+ end
+ end
+
+ if is_set("instrument.lbm") or true then
+ -- Wrap register_lbm() to automatically instrument lbms.
+ local orig_register_lbm = core.register_lbm
+ core.register_lbm = function(spec)
+ spec.action = instrument {
+ func = spec.action,
+ class = "LBM",
+ label = spec.label or spec.name,
+ }
+ orig_register_lbm(spec)
+ end
+ end
+
+ if is_set("instrument.global_callback") or true then
+ for func_name, _ in pairs(register_functions) do
+ core[func_name] = instrument_register(core[func_name], func_name)
+ end
+ end
+
+ if is_set("instrument.profiler") or false then
+ -- Measure overhead of instrumentation, but keep it down for functions
+ -- So keep the `return` for better optimization.
+ profiler.empty_instrument = instrument {
+ func = function() return end,
+ mod = "*profiler*",
+ class = "Instrumentation overhead",
+ label = false,
+ }
+ end
+end
+
+return {
+ register_functions = register_functions,
+ instrument = instrument,
+ init = init,
+ init_chatcommand = init_chatcommand,
+}
diff --git a/builtin/profiler/reporter.lua b/builtin/profiler/reporter.lua
new file mode 100644
index 000000000..5b38ed4df
--- /dev/null
+++ b/builtin/profiler/reporter.lua
@@ -0,0 +1,277 @@
+--Minetest
+--Copyright (C) 2016 T4im
+--
+--This program is free software; you can redistribute it and/or modify
+--it under the terms of the GNU Lesser General Public License as published by
+--the Free Software Foundation; either version 2.1 of the License, or
+--(at your option) any later version.
+--
+--This program is distributed in the hope that it will be useful,
+--but WITHOUT ANY WARRANTY; without even the implied warranty of
+--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+--GNU Lesser General Public License for more details.
+--
+--You should have received a copy of the GNU Lesser General Public License along
+--with this program; if not, write to the Free Software Foundation, Inc.,
+--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+local DIR_DELIM, LINE_DELIM = DIR_DELIM, "\n"
+local table, unpack, string, pairs, io, os = table, unpack, string, pairs, io, os
+local rep, sprintf, tonumber = string.rep, string.format, tonumber
+local core, setting_get = core, core.setting_get
+local reporter = {}
+
+---
+-- Shorten a string. End on an ellipsis if shortened.
+--
+local function shorten(str, length)
+ if str and str:len() > length then
+ return "..." .. str:sub(-(length-3))
+ end
+ return str
+end
+
+local function filter_matches(filter, text)
+ return not filter or string.match(text, filter)
+end
+
+local function format_number(number, fmt)
+ number = tonumber(number)
+ if not number then
+ return "N/A"
+ end
+ return sprintf(fmt or "%d", number)
+end
+
+local Formatter = {
+ new = function(self, object)
+ object = object or {}
+ object.out = {} -- output buffer
+ self.__index = self
+ return setmetatable(object, self)
+ end,
+ __tostring = function (self)
+ return table.concat(self.out, LINE_DELIM)
+ end,
+ print = function(self, text, ...)
+ if (...) then
+ text = sprintf(text, ...)
+ end
+
+ if text then
+ -- Avoid format unicode issues.
+ text = text:gsub("Ms", "µs")
+ end
+
+ table.insert(self.out, text or LINE_DELIM)
+ end,
+ flush = function(self)
+ table.insert(self.out, LINE_DELIM)
+ local text = table.concat(self.out, LINE_DELIM)
+ self.out = {}
+ return text
+ end
+}
+
+local widths = { 55, 9, 9, 9, 5, 5, 5 }
+local txt_row_format = sprintf(" %%-%ds | %%%ds | %%%ds | %%%ds | %%%ds | %%%ds | %%%ds", unpack(widths))
+
+local HR = {}
+for i=1, #widths do
+ HR[i]= rep("-", widths[i])
+end
+-- ' | ' should break less with github than '-+-', when people are pasting there
+HR = sprintf("-%s-", table.concat(HR, " | "))
+
+local TxtFormatter = Formatter:new {
+ format_row = function(self, modname, instrument_name, statistics)
+ local label
+ if instrument_name then
+ label = shorten(instrument_name, widths[1] - 5)
+ label = sprintf(" - %s %s", label, rep(".", widths[1] - 5 - label:len()))
+ else -- Print mod_stats
+ label = shorten(modname, widths[1] - 2) .. ":"
+ end
+
+ self:print(txt_row_format, label,
+ format_number(statistics.time_min),
+ format_number(statistics.time_max),
+ format_number(statistics:get_time_avg()),
+ format_number(statistics.part_min, "%.1f"),
+ format_number(statistics.part_max, "%.1f"),
+ format_number(statistics:get_part_avg(), "%.1f")
+ )
+ end,
+ format = function(self, filter)
+ local profile = self.profile
+ self:print("Values below show absolute/relative times spend per server step by the instrumented function.")
+ self:print("A total of %d samples were taken", profile.stats_total.samples)
+
+ if filter then
+ self:print("The output is limited to '%s'", filter)
+ end
+
+ self:print()
+ self:print(
+ txt_row_format,
+ "instrumentation", "min Ms", "max Ms", "avg Ms", "min %", "max %", "avg %"
+ )
+ self:print(HR)
+ for modname,mod_stats in pairs(profile.stats) do
+ if filter_matches(filter, modname) then
+ self:format_row(modname, nil, mod_stats)
+
+ if mod_stats.instruments ~= nil then
+ for instrument_name, instrument_stats in pairs(mod_stats.instruments) do
+ self:format_row(nil, instrument_name, instrument_stats)
+ end
+ end
+ end
+ end
+ self:print(HR)
+ if not filter then
+ self:format_row("total", nil, profile.stats_total)
+ end
+ end
+}
+
+local CsvFormatter = Formatter:new {
+ format_row = function(self, modname, instrument_name, statistics)
+ self:print(
+ "%q,%q,%d,%d,%d,%d,%d,%f,%f,%f",
+ modname, instrument_name,
+ statistics.samples,
+ statistics.time_min,
+ statistics.time_max,
+ statistics:get_time_avg(),
+ statistics.time_all,
+ statistics.part_min,
+ statistics.part_max,
+ statistics:get_part_avg()
+ )
+ end,
+ format = function(self, filter)
+ self:print(
+ "%q,%q,%q,%q,%q,%q,%q,%q,%q,%q",
+ "modname", "instrumentation",
+ "samples",
+ "time min µs",
+ "time max µs",
+ "time avg µs",
+ "time all µs",
+ "part min %",
+ "part max %",
+ "part avg %"
+ )
+ for modname, mod_stats in pairs(self.profile.stats) do
+ if filter_matches(filter, modname) then
+ self:format_row(modname, "*", mod_stats)
+
+ if mod_stats.instruments ~= nil then
+ for instrument_name, instrument_stats in pairs(mod_stats.instruments) do
+ self:format_row(modname, instrument_name, instrument_stats)
+ end
+ end
+ end
+ end
+ end
+}
+
+local function format_statistics(profile, format, filter)
+ local formatter
+ if format == "csv" then
+ formatter = CsvFormatter:new {
+ profile = profile
+ }
+ else
+ formatter = TxtFormatter:new {
+ profile = profile
+ }
+ end
+ formatter:format(filter)
+ return formatter:flush()
+end
+
+---
+-- Format the profile ready for display and
+-- @return string to be printed to the console
+--
+function reporter.print(profile, filter)
+ if filter == "" then filter = nil end
+ return format_statistics(profile, "txt", filter)
+end
+
+---
+-- Serialize the profile data and
+-- @return serialized data to be saved to a file
+--
+local function serialize_profile(profile, format, filter)
+ if format == "lua" or format == "json" or format == "json_pretty" then
+ local stats = filter and {} or profile.stats
+ if filter then
+ for modname, mod_stats in pairs(profile.stats) do
+ if filter_matches(filter, modname) then
+ stats[modname] = mod_stats
+ end
+ end
+ end
+ if format == "lua" then
+ return core.serialize(stats)
+ elseif format == "json" then
+ return core.write_json(stats)
+ elseif format == "json_pretty" then
+ return core.write_json(stats, true)
+ end
+ end
+ -- Fall back to textual formats.
+ return format_statistics(profile, format, filter)
+end
+
+local worldpath = core.get_worldpath()
+local function get_save_path(format, filter)
+ local report_path = setting_get("profiler.report_path") or ""
+ if report_path ~= "" then
+ core.mkdir(sprintf("%s%s%s", worldpath, DIR_DELIM, report_path))
+ end
+ return (sprintf(
+ "%s/%s/profile-%s%s.%s",
+ worldpath,
+ report_path,
+ os.date("%Y%m%dT%H%M%S"),
+ filter and ("-" .. filter) or "",
+ format
+ ):gsub("[/\\]+", DIR_DELIM))-- Clean up delims
+end
+
+---
+-- Save the profile to the world path.
+-- @return success, log message
+--
+function reporter.save(profile, format, filter)
+ if not format or format == "" then
+ format = setting_get("profiler.default_report_format") or "txt"
+ end
+ if filter == "" then
+ filter = nil
+ end
+
+ local path = get_save_path(format, filter)
+
+ local output, io_err = io.open(path, "w")
+ if not output then
+ return false, "Saving of profile failed with: " .. io_err
+ end
+ local content, err = serialize_profile(profile, format, filter)
+ if not content then
+ output:close()
+ return false, "Saving of profile failed with: " .. err
+ end
+ output:write(content)
+ output:close()
+
+ local logmessage = "Profile saved to " .. path
+ core.log("action", logmessage)
+ return true, logmessage
+end
+
+return reporter
diff --git a/builtin/profiler/sampling.lua b/builtin/profiler/sampling.lua
new file mode 100644
index 000000000..1d1ef256d
--- /dev/null
+++ b/builtin/profiler/sampling.lua
@@ -0,0 +1,206 @@
+--Minetest
+--Copyright (C) 2016 T4im
+--
+--This program is free software; you can redistribute it and/or modify
+--it under the terms of the GNU Lesser General Public License as published by
+--the Free Software Foundation; either version 2.1 of the License, or
+--(at your option) any later version.
+--
+--This program is distributed in the hope that it will be useful,
+--but WITHOUT ANY WARRANTY; without even the implied warranty of
+--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+--GNU Lesser General Public License for more details.
+--
+--You should have received a copy of the GNU Lesser General Public License along
+--with this program; if not, write to the Free Software Foundation, Inc.,
+--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+local setmetatable = setmetatable
+local pairs, format = pairs, string.format
+local min, max, huge = math.min, math.max, math.huge
+local core = core
+
+local profiler = ...
+-- Split sampler and profile up, to possibly allow for rotation later.
+local sampler = {}
+local profile
+local stats_total
+local logged_time, logged_data
+
+local _stat_mt = {
+ get_time_avg = function(self)
+ return self.time_all/self.samples
+ end,
+ get_part_avg = function(self)
+ if not self.part_all then
+ return 100 -- Extra handling for "total"
+ end
+ return self.part_all/self.samples
+ end,
+}
+_stat_mt.__index = _stat_mt
+
+function sampler.reset()
+ -- Accumulated logged time since last sample.
+ -- This helps determining, the relative time a mod used up.
+ logged_time = 0
+ -- The measurements taken through instrumentation since last sample.
+ logged_data = {}
+
+ profile = {
+ -- Current mod statistics (max/min over the entire mod lifespan)
+ -- Mod specific instrumentation statistics are nested within.
+ stats = {},
+ -- Current stats over all mods.
+ stats_total = setmetatable({
+ samples = 0,
+ time_min = huge,
+ time_max = 0,
+ time_all = 0,
+ part_min = 100,
+ part_max = 100
+ }, _stat_mt)
+ }
+ stats_total = profile.stats_total
+
+ -- Provide access to the most recent profile.
+ sampler.profile = profile
+end
+
+---
+-- Log a measurement for the sampler to pick up later.
+-- Keep `log` and its often called functions lean.
+-- It will directly add to the instrumentation overhead.
+--
+function sampler.log(modname, instrument_name, time_diff)
+ if time_diff <= 0 then
+ if time_diff < 0 then
+ -- This **might** have happened on a semi-regular basis with huge mods,
+ -- resulting in negative statistics (perhaps midnight time jumps or ntp corrections?).
+ core.log("warning", format(
+ "Time travel of %s::%s by %dµs.",
+ modname, instrument_name, time_diff
+ ))
+ end
+ -- Throwing these away is better, than having them mess with the overall result.
+ return
+ end
+
+ local mod_data = logged_data[modname]
+ if mod_data == nil then
+ mod_data = {}
+ logged_data[modname] = mod_data
+ end
+
+ mod_data[instrument_name] = (mod_data[instrument_name] or 0) + time_diff
+ -- Update logged time since last sample.
+ logged_time = logged_time + time_diff
+end
+
+---
+-- Return a requested statistic.
+-- Initialize if necessary.
+--
+local function get_statistic(stats_table, name)
+ local statistic = stats_table[name]
+ if statistic == nil then
+ statistic = setmetatable({
+ samples = 0,
+ time_min = huge,
+ time_max = 0,
+ time_all = 0,
+ part_min = 100,
+ part_max = 0,
+ part_all = 0,
+ }, _stat_mt)
+ stats_table[name] = statistic
+ end
+ return statistic
+end
+
+---
+-- Update a statistic table
+--
+local function update_statistic(stats_table, time)
+ stats_table.samples = stats_table.samples + 1
+
+ -- Update absolute time (µs) spend by the subject
+ stats_table.time_min = min(stats_table.time_min, time)
+ stats_table.time_max = max(stats_table.time_max, time)
+ stats_table.time_all = stats_table.time_all + time
+
+ -- Update relative time (%) of this sample spend by the subject
+ local current_part = (time/logged_time) * 100
+ stats_table.part_min = min(stats_table.part_min, current_part)
+ stats_table.part_max = max(stats_table.part_max, current_part)
+ stats_table.part_all = stats_table.part_all + current_part
+end
+
+---
+-- Sample all logged measurements each server step.
+-- Like any globalstep function, this should not be too heavy,
+-- but does not add to the instrumentation overhead.
+--
+local function sample(dtime)
+ -- Rare, but happens and is currently of no informational value.
+ if logged_time == 0 then
+ return
+ end
+
+ for modname, instruments in pairs(logged_data) do
+ local mod_stats = get_statistic(profile.stats, modname)
+ if mod_stats.instruments == nil then
+ -- Current statistics for each instrumentation component
+ mod_stats.instruments = {}
+ end
+
+ local mod_time = 0
+ for instrument_name, time in pairs(instruments) do
+ if time > 0 then
+ mod_time = mod_time + time
+ local instrument_stats = get_statistic(mod_stats.instruments, instrument_name)
+
+ -- Update time of this sample spend by the instrumented function.
+ update_statistic(instrument_stats, time)
+ -- Reset logged data for the next sample.
+ instruments[instrument_name] = 0
+ end
+ end
+
+ -- Update time of this sample spend by this mod.
+ update_statistic(mod_stats, mod_time)
+ end
+
+ -- Update the total time spend over all mods.
+ stats_total.time_min = min(stats_total.time_min, logged_time)
+ stats_total.time_max = max(stats_total.time_max, logged_time)
+ stats_total.time_all = stats_total.time_all + logged_time
+
+ stats_total.samples = stats_total.samples + 1
+ logged_time = 0
+end
+
+---
+-- Setup empty profile and register the sampling function
+--
+function sampler.init()
+ sampler.reset()
+
+ if core.setting_getbool("instrument.profiler") then
+ core.register_globalstep(function()
+ if logged_time == 0 then
+ return
+ end
+ return profiler.empty_instrument()
+ end)
+ core.register_globalstep(profiler.instrument {
+ func = sample,
+ mod = "*profiler*",
+ class = "Sampler (update stats)",
+ label = false,
+ })
+ else
+ core.register_globalstep(sample)
+ end
+end
+
+return sampler
diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt
index ad269d8b0..1818b5a18 100644
--- a/builtin/settingtypes.txt
+++ b/builtin/settingtypes.txt
@@ -3,7 +3,7 @@
# General format:
# name (Readable name) type type_args
#
-# Note that the parts are seperated by exactly one space
+# Note that the parts are separated by exactly one space
#
# `type` can be:
# - int
@@ -70,7 +70,7 @@ fast_move (Fast movement) bool false
# This requires the "noclip" privilege on the server.
noclip (Noclip) bool false
-# Smooths camera when moving and looking around.
+# Smooths camera when looking around. Also called look or mouse smoothing.
# Useful for recording videos.
cinematic (Cinematic mode) bool false
@@ -104,6 +104,17 @@ random_input (Random input) bool false
# Continuous forward movement (only used for testing).
continuous_forward (Continuous forward) bool false
+# Enable Joysticks
+enable_joysticks (Enable Joysticks) bool false
+
+# The time in seconds it takes between repeated events
+# when holding down a joystick button combination.
+repeat_joystick_button_time (Joystick button repetition interval) float 0.17
+
+# The sensitivity of the joystick axes for moving the
+# ingame view frustum around.
+joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170
+
# Key for moving the player forward.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_forward (Forward key) key KEY_KEY_W
@@ -165,9 +176,13 @@ keymap_fastmove (Fast key) key KEY_KEY_J
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_noclip (Noclip key) key KEY_KEY_H
+# Key for toggling autorun.
+# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
+keymap_autorun (Autorun key) key
+
# Key for toggling cinematic mode.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
-keymap_cinematic (Cinematic mode key) key KEY_F8
+keymap_cinematic (Cinematic mode key) key
# Key for toggling display of minimap.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
@@ -193,7 +208,7 @@ keymap_toggle_chat (Chat toggle key) key KEY_F2
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_toggle_force_fog_off (Fog toggle key) key KEY_F3
-# Key for toggling the camrea update. Only used for development
+# Key for toggling the camera update. Only used for development
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_toggle_update_camera (Camera update toggle key) key
@@ -236,7 +251,7 @@ remote_port (Remote port) int 30000 1 65535
# Enable if you want to connect to 0.4.12 servers and before.
# Servers starting with 0.4.13 will work, 0.4.12-dev servers may work.
# Disabling this option will protect your password better.
-send_pre_v25_init (Support older servers) bool true
+send_pre_v25_init (Support older servers) bool false
# Save the map received by the client on disk.
enable_local_map_saving (Saving map received from server) bool false
@@ -324,7 +339,7 @@ fsaa (FSAA) enum 0 0,1,2,4,8,16
[***Shaders]
-# Shaders allow advanced visul effects and may increase performance on some video cards.
+# Shaders allow advanced visual effects and may increase performance on some video cards.
# Thy only work with the OpenGL video backend.
enable_shaders (Shaders) bool true
@@ -403,8 +418,7 @@ fps_max (Maximum FPS) int 60
pause_fps_max (FPS in pause menu) int 20
# View distance in nodes.
-# Min = 20
-viewing_range (Viewing range) int 100
+viewing_range (Viewing range) int 100 20 4000
# Width component of the initial window size.
screenW (Screen width) int 800
@@ -424,6 +438,10 @@ vsync (V-Sync) bool false
# Field of view in degrees.
fov (Field of view) int 72 30 160
+# Field of view while zooming in degrees.
+# This requires the "zoom" privilege on the server.
+zoom_fov (Field of view for zoom) int 15 15 160
+
# Adjust the gamma encoding for the light tables. Lower numbers are brighter.
# This setting is for the client only and is ignored by the server.
display_gamma (Gamma) float 1.8 1.0 3.0
@@ -510,6 +528,9 @@ ambient_occlusion_gamma (Ambient occlusion gamma) float 2.2 0.25 4.0
# Enables animation of inventory items.
inventory_items_animations (Inventory items animations) bool false
+# Fraction of the visible distance at which fog starts to be rendered
+fog_start (Fog Start) float 0.4 0.0 0.99
+
[**Menus]
# Use a cloud animation for the main menu background.
@@ -530,7 +551,7 @@ gui_scaling_filter (GUI scaling filter) bool false
# When gui_scaling_filter_txr2img is true, copy those images
# from hardware to software for scaling. When false, fall back
# to the old scaling method, for video drivers that don't
-# propery support downloading textures back from hardware.
+# properly support downloading textures back from hardware.
gui_scaling_filter_txr2img (GUI scaling filter txr2img) bool true
# Delay showing tooltips, stated in milliseconds.
@@ -542,13 +563,13 @@ freetype (Freetype fonts) bool true
# Path to TrueTypeFont or bitmap.
font_path (Font path) path fonts/liberationsans.ttf
-font_size (Font size) int 15
+font_size (Font size) int 16
# Font shadow offset, if 0 then shadow will not be drawn.
font_shadow (Font shadow) int 1
# Font shadow alpha (opaqueness, between 0 and 255).
-font_shadow_alpha (Font shadow alpha) int 128 0 255
+font_shadow_alpha (Font shadow alpha) int 127 0 255
mono_font_path (Monospace font path) path fonts/liberationmono.ttf
@@ -615,6 +636,11 @@ server_announce (Announce server) bool false
# If you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net.
serverlist_url (Serverlist URL) string servers.minetest.net
+# Disable escape sequences, e.g. chat coloring.
+# Use this if you want to run a server with pre-0.4.14 clients and you want to disable
+# the escape sequences generated by mods.
+disable_escape_sequences (Disable escape sequences) bool false
+
[*Network]
# Network port to listen (UDP).
@@ -642,15 +668,15 @@ ipv6_server (IPv6 server) bool false
[**Advanced]
-# How many blocks are flying in the wire simultaneously per client.
-max_simultaneous_block_sends_per_client (Maximum simultaneously blocks send per client) int 10
+# Maximum number of blocks that are simultaneously sent per client.
+max_simultaneous_block_sends_per_client (Maximum simultaneous block sends per client) int 10
-# How many blocks are flying in the wire simultaneously for the whole server.
-max_simultaneous_block_sends_server_total (Maximum simultaneously bocks send total) int 40
+# Maximum number of blocks that are simultaneously sent in total.
+max_simultaneous_block_sends_server_total (Maximum simultaneous block sends total) int 40
# To reduce lag, block transfers are slowed down when a player is building something.
# This determines how long they are slowed down after placing or removing a node.
-full_block_send_enable_min_time_from_building () float 2.0
+full_block_send_enable_min_time_from_building (Delay in sending blocks after building) float 2.0
# Maximum number of packets sent per send step, if you have a slow connection
# try reducing it, but don't reduce it to a number below double of targeted
@@ -750,6 +776,15 @@ time_speed (Time speed) int 72
# Interval of saving important changes in the world, stated in seconds.
server_map_save_interval (Map save interval) float 5.3
+# Set the maximum character length of a chat message sent by clients.
+# chat_message_max_size int 500
+
+# Limit a single player to send X messages per 10 seconds.
+# chat_message_limit_per_10sec float 10.0
+
+# Kick player if send more than X messages per 10 seconds.
+# chat_message_limit_trigger_kick int 50
+
[**Physics]
movement_acceleration_default (Default acceleration) float 3
@@ -774,15 +809,6 @@ movement_gravity (Gravity) float 9.81
# - error: abort on usage of deprecated call (suggested for mod developers).
deprecated_lua_api_handling (Deprecated Lua API handling) enum legacy legacy,log,error
-# Useful for mod developers.
-mod_profiling (Mod profiling) bool false
-
-# Detailed mod profile data. Useful for mod developers.
-detailed_profiling (Detailed mod profiling) bool false
-
-# Profiler data print interval. 0 = disable. Useful for developers.
-profiler_print_interval (Profiling print interval) int 0
-
# Number of extra blocks that can be loaded by /clearobjects at once.
# This is a trade-off between sqlite transaction overhead and
# memory consumption (4096=100MB, as a rule of thumb).
@@ -793,7 +819,7 @@ max_clearobjects_extra_loaded_blocks (Max. clearobjects extra blocks) int 4096
server_unload_unused_data_timeout (Unload unused server data) int 29
# Maximum number of statically stored objects in a block.
-max_objects_per_block (Maxmimum objects per block) int 49
+max_objects_per_block (Maximum objects per block) int 64
# See http://www.sqlite.org/pragma.html#pragma_synchronous
sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2
@@ -825,11 +851,18 @@ liquid_queue_purge_time (Liquid queue purge time) int 0
# Liquid update interval in seconds.
liquid_update (Liquid update tick) float 1.0
+# At this distance the server will aggressively optimize which blocks are sent to clients.
+# Small values potentially improve performance a lot, at the expense of visible rendering glitches.
+# (some blocks will not be rendered under water and in caves, as well as sometimes on land)
+# Setting this to a value greater than max_block_send_distance disables this optimization.
+# Stated in mapblocks (16 nodes)
+block_send_optimize_distance (block send optimize distance) int 4 2
+
[*Mapgen]
# Name of map generator to be used when creating a new world.
# Creating a world in the main menu will override this.
-mg_name (Mapgen name) enum v6 v5,v6,v7,flat,valleys,fractal,singlenode
+mg_name (Mapgen name) enum v7 v5,v6,v7,flat,valleys,fractal,singlenode
# Water surface level of the world.
water_level (Water level) int 1
@@ -848,8 +881,6 @@ map_generation_limit (Map generation limit) int 31000 0 31000
# Global map generation attributes.
# In Mapgen v6 the 'decorations' flag controls all decorations except trees
# and junglegrass, in all other mapgens this flag controls all decorations.
-# The default flags set in the engine are: caves, light, decorations
-# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with 'no' are used to explicitly disable them.
mg_flags (Mapgen flags) flags caves,dungeons,light,decorations caves,dungeons,light,decorations,nocaves,nodungeons,nolight,nodecorations
@@ -910,8 +941,6 @@ mgv5_np_cave2 (Mapgen v5 cave2 noise parameters) noise_params 0, 12, (50, 50, 50
# Map generation attributes specific to Mapgen v6.
# When snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored.
-# The default flags set in the engine are: biomeblend, mudflow
-# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with 'no' are used to explicitly disable them.
mgv6_spflags (Mapgen v6 flags) flags jungles,biomeblend,mudflow,snowbiomes,trees jungles,biomeblend,mudflow,snowbiomes,flat,trees,nojungles,nobiomeblend,nomudflow,nosnowbiomes,noflat,notrees
@@ -936,15 +965,27 @@ mgv6_np_apple_trees (Mapgen v6 apple trees noise parameters) noise_params 0, 1,
[***Mapgen v7]
# Map generation attributes specific to Mapgen v7.
-# The 'ridges' flag controls the rivers.
-# The default flags set in the engine are: mountains, ridges
-# The flags string modifies the engine defaults.
+# The 'ridges' flag enables the rivers.
+# Floatlands are currently experimental and subject to change.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with 'no' are used to explicitly disable them.
-mgv7_spflags (Mapgen v7 flags) flags mountains,ridges mountains,ridges,nomountains,noridges
+mgv7_spflags (Mapgen v7 flags) flags mountains,ridges mountains,ridges,floatlands,nomountains,noridges,nofloatlands
# Controls width of tunnels, a smaller value creates wider tunnels.
-mgv7_cave_width (Mapgen v7 cave width) float 0.3
+mgv7_cave_width (Mapgen v7 cave width) float 0.09
+
+# Controls the density of floatland mountain terrain.
+# Is an offset added to the 'np_mountain' noise value.
+mgv7_float_mount_density (Mapgen v7 floatland mountain density) float 0.6
+
+# Typical maximum height, above and below midpoint, of floatland mountain terrain.
+mgv7_float_mount_height (Mapgen v7 floatland mountain height) float 128.0
+
+# Y-level of floatland midpoint and lake surface.
+mgv7_floatland_level (Mapgen v7 floatland level) int 1280
+
+# Y-level to which floatland shadows extend.
+mgv7_shadow_limit (Mapgen v7 shadow limit) int 1024
mgv7_np_terrain_base (Mapgen v7 terrain base noise parameters) noise_params 4, 70, (600, 600, 600), 82341, 5, 0.6, 2.0
mgv7_np_terrain_alt (Mapgen v7 terrain altitude noise parameters) noise_params 4, 25, (600, 600, 600), 5934, 5, 0.6, 2.0
@@ -952,18 +993,18 @@ mgv7_np_terrain_persist (Mapgen v7 terrain persistation noise parameters) noise_
mgv7_np_height_select (Mapgen v7 height select noise parameters) noise_params -8, 16, (500, 500, 500), 4213, 6, 0.7, 2.0
mgv7_np_filler_depth (Mapgen v7 filler depth noise parameters) noise_params 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0
mgv7_np_mount_height (Mapgen v7 mount height noise parameters) noise_params 256, 112, (1000, 1000, 1000), 72449, 3, 0.6, 2.0
-mgv7_np_ridge_uwater (Mapgen v7 ridge water noise parameters) noise_params 0, 1, (1000, 1000, 1000), 85039, 5, 0.6, 2.0
+mgv7_np_ridge_uwater (Mapgen v7 river course noise parameters) noise_params 0, 1, (1000, 1000, 1000), 85039, 5, 0.6, 2.0
+mgv7_np_floatland_base (Mapgen v7 floatland base terrain noise parameters) noise_params -0.6, 1.5, (600, 600, 600), 114, 5, 0.6, 2.0
+mgv7_np_float_base_height (Mapgen v7 floatland base terrain height noise parameters) noise_params 48, 24, (300, 300, 300), 907, 4, 0.7, 2.0
mgv7_np_mountain (Mapgen v7 mountain noise parameters) noise_params -0.6, 1, (250, 350, 250), 5333, 5, 0.63, 2.0
-mgv7_np_ridge (Mapgen v7 ridge noise parameters) noise_params 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0
-mgv7_np_cave1 (Mapgen v7 cave1 noise parameters) noise_params 0, 12, (100, 100, 100), 52534, 4, 0.5, 2.0
-mgv7_np_cave2 (Mapgen v7 cave2 noise parameters) noise_params 0, 12, (100, 100, 100), 10325, 4, 0.5, 2.0
+mgv7_np_ridge (Mapgen v7 river channel wall noise parameters) noise_params 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0
+mgv7_np_cave1 (Mapgen v7 cave1 noise parameters) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0
+mgv7_np_cave2 (Mapgen v7 cave2 noise parameters) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0
[***Mapgen flat]
# Map generation attributes specific to Mapgen flat.
# Occasional lakes and hills can be added to the flat world.
-# The default flags set in the engine are: none
-# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with 'no' are used to explicitly disable them.
mgflat_spflags (Mapgen flat flags) flags lakes,hills,,nolakes,nohills
@@ -975,7 +1016,7 @@ mgflat_ground_level (Mapgen flat ground level) int 8
mgflat_large_cave_depth (Mapgen flat large cave depth) int -33
# Controls width of tunnels, a smaller value creates wider tunnels.
-mgflat_cave_width (Mapgen flat cave width) float 0.3
+mgflat_cave_width (Mapgen flat cave width) float 0.09
# Terrain noise threshold for lakes.
# Controls proportion of world area covered by lakes.
@@ -999,13 +1040,13 @@ mgflat_hill_steepness (Mapgen flat hill steepness) float 64.0
mgflat_np_terrain (Mapgen flat terrain noise parameters) noise_params 0, 1, (600, 600, 600), 7244, 5, 0.6, 2.0
mgflat_np_filler_depth (Mapgen flat filler depth noise parameters) noise_params 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0
-mgflat_np_cave1 (Mapgen flat cave1 noise parameters) noise_params 0, 12, (128, 128, 128), 52534, 4, 0.5, 2.0
-mgflat_np_cave2 (Mapgen flat cave2 noise parameters) noise_params 0, 12, (128, 128, 128), 10325, 4, 0.5, 2.0
+mgflat_np_cave1 (Mapgen flat cave1 noise parameters) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0
+mgflat_np_cave2 (Mapgen flat cave2 noise parameters) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0
[***Mapgen fractal]
# Controls width of tunnels, a smaller value creates wider tunnels.
-mgfractal_cave_width (Mapgen fractal cave width) float 0.3
+mgfractal_cave_width (Mapgen fractal cave width) float 0.09
# Choice of 18 fractals from 9 formulas.
# 1 = 4D "Roundy" mandelbrot set.
@@ -1066,8 +1107,8 @@ mgfractal_julia_w (Mapgen fractal julia w) float 0.33
mgfractal_np_seabed (Mapgen fractal seabed noise parameters) noise_params -14, 9, (600, 600, 600), 41900, 5, 0.6, 2.0
mgfractal_np_filler_depth (Mapgen fractal filler depth noise parameters) noise_params 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0
-mgfractal_np_cave1 (Mapgen fractal cave1 noise parameters) noise_params 0, 12, (128, 128, 128), 52534, 4, 0.5, 2.0
-mgfractal_np_cave2 (Mapgen fractal cave2 noise parameters) noise_params 0, 12, (128, 128, 128), 10325, 4, 0.5, 2.0
+mgfractal_np_cave1 (Mapgen fractal cave1 noise parameters) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0
+mgfractal_np_cave2 (Mapgen fractal cave2 noise parameters) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0
# Mapgen Valleys parameters
[***Mapgen Valleys]
@@ -1079,8 +1120,6 @@ mgfractal_np_cave2 (Mapgen fractal cave2 noise parameters) noise_params 0, 12, (
# 'altitude_chill' makes higher elevations colder, which may cause biome issues.
# 'humid_rivers' modifies the humidity around rivers and in areas where water would tend to pool,
# it may interfere with delicately adjusted biomes.
-# The default flags set in the engine are: altitude_chill, humid_rivers
-# The flags string modifies the engine defaults.
# Flags that are not specified in the flag string are not modified from the default.
# Flags starting with 'no' are used to explicitly disable them.
mg_valleys_spflags (Valleys C Flags) flags altitude_chill,humid_rivers altitude_chill,noaltitude_chill,humid_rivers,nohumid_rivers
@@ -1109,16 +1148,16 @@ mgvalleys_river_size (River Size) int 5
mgvalleys_water_features (Water Features) int 0
# Controls width of tunnels, a smaller value creates wider tunnels.
-mgvalleys_cave_width (Cave width) float 0.3
+mgvalleys_cave_width (Cave width) float 0.09
# Noise parameters
[****Noises]
# Caves and tunnels form at the intersection of the two noises
-mgvalleys_np_cave1 (Cave noise #1) noise_params 0, 12, (100, 100, 100), 52534, 4, 0.5, 2.0
+mgvalleys_np_cave1 (Cave noise #1) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0
# Caves and tunnels form at the intersection of the two noises
-mgvalleys_np_cave2 (Cave noise #2) noise_params 0, 12, (100, 100, 100), 10325, 4, 0.5, 2.0
+mgvalleys_np_cave2 (Cave noise #2) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0
# The depth of dirt or other filler
mgvalleys_np_filler_depth (Filler Depth) noise_params 0, 1.2, (256, 256, 256), 1605, 3, 0.5, 2.0
@@ -1153,10 +1192,55 @@ secure.enable_security (Enable mod security) bool false
# functions even when mod security is on (via request_insecure_environment()).
secure.trusted_mods (Trusted mods) string
-# Comma-seperated list of mods that are allowed to access HTTP APIs, which
+# Comma-separated list of mods that are allowed to access HTTP APIs, which
# allow them to upload and download data to/from the internet.
secure.http_mods (HTTP Mods) string
+[*Advanced]
+
+[**Profiling]
+# Load the game profiler to collect game profiling data.
+# Provides a /profiler command to access the compiled profile.
+# Useful for mod developers and server operators.
+profiler.load (Load the game profiler) bool false
+
+# The default format in which profiles are being saved,
+# when calling `/profiler save [format]` without format.
+profiler.default_report_format (Default report format) enum txt txt,csv,lua,json,json_pretty
+
+# The file path relative to your worldpath in which profiles will be saved to.
+#
+profiler.report_path (Report path) string ""
+
+[***Instrumentation]
+
+# Instrument the methods of entities on registration.
+instrument.entity (Entity methods) bool true
+
+# Instrument the action function of Active Block Modifiers on registration.
+instrument.abm (Active Block Modifiers) bool true
+
+# Instrument the action function of Loading Block Modifiers on registration.
+instrument.lbm (Loading Block Modifiers) bool true
+
+# Instrument chatcommands on registration.
+instrument.chatcommand (Chatcommands) bool true
+
+# Instrument global callback functions on registration.
+# (anything you pass to a minetest.register_*() function)
+instrument.global_callback (Global callbacks) bool true
+
+[****Advanced]
+# Instrument builtin.
+# This is usually only needed by core/builtin contributors
+instrument.builtin (Builtin) bool false
+
+# Have the profiler instrument itself:
+# * Instrument an empty function.
+# This estimates the overhead, that instrumentation is adding (+1 function call).
+# * Instrument the sampler being used to update the statistics.
+instrument.profiler (Profiler) bool false
+
[Client and Server]
# Name of the player.
@@ -1166,7 +1250,7 @@ name (Player name) string
# Set the language. Leave empty to use the system language.
# A restart is required after changing this.
-language (Language) enum ,be,cs,da,de,eo,es,et,fr,hu,id,it,ja,jbo,ko,ky,lt,nb,nl,pl,pt,pt_BR,ro,ru,tr,uk,zh_CN,zh_TW
+language (Language) enum ,be,ca,cs,da,de,en,eo,es,et,fr,he,hu,id,it,ja,jbo,ko,ky,lt,nb,nl,pl,pt,pt_BR,ro,ru,sr_Cyrl,tr,uk,zh_CN,zh_TW
# Level of logging to be written to debug.txt:
# - <nothing> (no logging)
@@ -1212,3 +1296,6 @@ modstore_download_url (Modstore download URL) string https://forum.minetest.net/
modstore_listmods_url (Modstore mods list URL) string https://forum.minetest.net/mmdb/mods/
modstore_details_url (Modstore details URL) string https://forum.minetest.net/mmdb/mod/*/
+
+# Print the engine's profiling data in regular intervals (in seconds). 0 = disable. Useful for developers.
+profiler_print_interval (Engine profiling data print interval) int 0