diff options
author | est31 <MTest31@outlook.com> | 2016-12-22 23:16:00 +0100 |
---|---|---|
committer | est31 <MTest31@outlook.com> | 2016-12-22 23:16:00 +0100 |
commit | 81d56b94919dceb7b2e51d70b21a7ca22f852bd5 (patch) | |
tree | 1e9ef1be1b3295a8673d6e4f0bdeb4c2d3a6015f /builtin | |
parent | 8077612dcb48221281e726a60eb97bf73fde462b (diff) | |
parent | 231ac33d34dfaaddf292c5f31b1eae43eeefba2d (diff) | |
download | minetest-81d56b94919dceb7b2e51d70b21a7ca22f852bd5.tar.gz minetest-81d56b94919dceb7b2e51d70b21a7ca22f852bd5.tar.bz2 minetest-81d56b94919dceb7b2e51d70b21a7ca22f852bd5.zip |
Merge 0.4.15 changes into stable-0.4
0.4.15 release!
Diffstat (limited to 'builtin')
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 |