diff options
Diffstat (limited to 'builtin')
49 files changed, 3262 insertions, 2008 deletions
diff --git a/builtin/async/game.lua b/builtin/async/game.lua new file mode 100644 index 000000000..6512f0706 --- /dev/null +++ b/builtin/async/game.lua @@ -0,0 +1,59 @@ +core.log("info", "Initializing asynchronous environment (game)") + +local function pack2(...) + return {n=select('#', ...), ...} +end + +-- Entrypoint to run async jobs, called by C++ +function core.job_processor(func, params) + local retval = pack2(func(unpack(params, 1, params.n))) + + return retval +end + +-- Import a bunch of individual files from builtin/game/ +local gamepath = core.get_builtin_path() .. "game" .. DIR_DELIM + +dofile(gamepath .. "constants.lua") +dofile(gamepath .. "item_s.lua") +dofile(gamepath .. "misc_s.lua") +dofile(gamepath .. "features.lua") +dofile(gamepath .. "voxelarea.lua") + +-- Transfer of globals +do + local all = assert(core.transferred_globals) + core.transferred_globals = nil + + all.registered_nodes = {} + all.registered_craftitems = {} + all.registered_tools = {} + for k, v in pairs(all.registered_items) do + -- Disable further modification + setmetatable(v, {__newindex = {}}) + -- Reassemble the other tables + if v.type == "node" then + all.registered_nodes[k] = v + elseif v.type == "craftitem" then + all.registered_craftitems[k] = v + elseif v.type == "tool" then + all.registered_tools[k] = v + end + end + + for k, v in pairs(all) do + core[k] = v + end +end + +-- For tables that are indexed by item name: +-- If table[X] does not exist, default to table[core.registered_aliases[X]] +local alias_metatable = { + __index = function(t, name) + return rawget(t, core.registered_aliases[name]) + end +} +setmetatable(core.registered_items, alias_metatable) +setmetatable(core.registered_nodes, alias_metatable) +setmetatable(core.registered_craftitems, alias_metatable) +setmetatable(core.registered_tools, alias_metatable) diff --git a/builtin/async/init.lua b/builtin/async/mainmenu.lua index 3803994d6..0e9c222d1 100644 --- a/builtin/async/init.lua +++ b/builtin/async/mainmenu.lua @@ -1,5 +1,4 @@ - -core.log("info", "Initializing Asynchronous environment") +core.log("info", "Initializing asynchronous environment") function core.job_processor(func, serialized_param) local param = core.deserialize(serialized_param) @@ -8,4 +7,3 @@ function core.job_processor(func, serialized_param) return retval or core.serialize(nil) end - diff --git a/builtin/client/init.lua b/builtin/client/init.lua index 589fe8f24..3719a90ee 100644 --- a/builtin/client/init.lua +++ b/builtin/client/init.lua @@ -5,6 +5,8 @@ local commonpath = scriptpath.."common"..DIR_DELIM dofile(clientpath .. "register.lua") dofile(commonpath .. "after.lua") +dofile(commonpath .. "mod_storage.lua") dofile(commonpath .. "chatcommands.lua") dofile(clientpath .. "chatcommands.lua") dofile(clientpath .. "death_formspec.lua") +dofile(clientpath .. "misc.lua") diff --git a/builtin/client/misc.lua b/builtin/client/misc.lua new file mode 100644 index 000000000..80e0f2904 --- /dev/null +++ b/builtin/client/misc.lua @@ -0,0 +1,7 @@ +function core.setting_get_pos(name) + local value = core.settings:get(name) + if not value then + return nil + end + return core.string_to_pos(value) +end diff --git a/builtin/common/information_formspecs.lua b/builtin/common/information_formspecs.lua index 3405263bf..1445a017c 100644 --- a/builtin/common/information_formspecs.lua +++ b/builtin/common/information_formspecs.lua @@ -22,7 +22,6 @@ local LIST_FORMSPEC_DESCRIPTION = [[ local F = core.formspec_escape local S = core.get_translator("__builtin") -local check_player_privs = core.check_player_privs -- CHAT COMMANDS FORMSPEC @@ -58,10 +57,11 @@ local function build_chatcommands_formspec(name, sel, copy) .. "any entry in the list.").. "\n" .. S("Double-click to copy the entry to the chat history.") + local privs = core.get_player_privs(name) for i, data in ipairs(mod_cmds) do rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. "," for j, cmds in ipairs(data[2]) do - local has_priv = check_player_privs(name, cmds[2].privs) + local has_priv = privs[cmds[2].privs] rows[#rows + 1] = ("%s,1,%s,%s"):format( has_priv and COLOR_GREEN or COLOR_GRAY, cmds[1], F(cmds[2].params)) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index f5f89acd7..467f18804 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -204,7 +204,7 @@ end -------------------------------------------------------------------------------- function string:trim() - return (self:gsub("^%s*(.-)%s*$", "%1")) + return self:match("^%s*(.-)%s*$") end -------------------------------------------------------------------------------- @@ -245,16 +245,16 @@ function math.round(x) return math.ceil(x - 0.5) end - +local formspec_escapes = { + ["\\"] = "\\\\", + ["["] = "\\[", + ["]"] = "\\]", + [";"] = "\\;", + [","] = "\\," +} function core.formspec_escape(text) - if text ~= nil then - text = string.gsub(text,"\\","\\\\") - text = string.gsub(text,"%]","\\]") - text = string.gsub(text,"%[","\\[") - text = string.gsub(text,";","\\;") - text = string.gsub(text,",","\\,") - end - return text + -- Use explicit character set instead of dot here because it doubles the performance + return text and string.gsub(text, "[\\%[%];,]", formspec_escapes) end @@ -265,18 +265,21 @@ function core.wrap_text(text, max_length, as_table) return as_table and {text} or text end - for word in text:gmatch('%S+') do - local cur_length = #table.concat(line, ' ') - if cur_length > 0 and cur_length + #word + 1 >= max_length then + local line_length = 0 + for word in text:gmatch("%S+") do + if line_length > 0 and line_length + #word + 1 >= max_length then -- word wouldn't fit on current line, move to next line - table.insert(result, table.concat(line, ' ')) - line = {} + table.insert(result, table.concat(line, " ")) + line = {word} + line_length = #word + else + table.insert(line, word) + line_length = line_length + 1 + #word end - table.insert(line, word) end - table.insert(result, table.concat(line, ' ')) - return as_table and result or table.concat(result, '\n') + table.insert(result, table.concat(line, " ")) + return as_table and result or table.concat(result, "\n") end -------------------------------------------------------------------------------- @@ -425,54 +428,50 @@ function core.string_to_pos(value) return nil end - local x, y, z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") - if x and y and z then - x = tonumber(x) - y = tonumber(y) - z = tonumber(z) - return vector.new(x, y, z) - end - x, y, z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$") + value = value:match("^%((.-)%)$") or value -- strip parentheses + + local x, y, z = value:trim():match("^([%d.-]+)[,%s]%s*([%d.-]+)[,%s]%s*([%d.-]+)$") if x and y and z then x = tonumber(x) y = tonumber(y) z = tonumber(z) return vector.new(x, y, z) end + return nil end -------------------------------------------------------------------------------- -function core.string_to_area(value) - local p1, p2 = unpack(value:split(") (")) - if p1 == nil or p2 == nil then - return nil - end - p1 = core.string_to_pos(p1 .. ")") - p2 = core.string_to_pos("(" .. p2) - if p1 == nil or p2 == nil then - return nil +do + local rel_num_cap = "(~?-?%d*%.?%d*)" -- may be overly permissive as this will be tonumber'ed anyways + local num_delim = "[,%s]%s*" + local pattern = "^" .. table.concat({rel_num_cap, rel_num_cap, rel_num_cap}, num_delim) .. "$" + + local function parse_area_string(pos, relative_to) + local pp = {} + pp.x, pp.y, pp.z = pos:trim():match(pattern) + return core.parse_coordinates(pp.x, pp.y, pp.z, relative_to) end - return p1, p2 -end + function core.string_to_area(value, relative_to) + local p1, p2 = value:match("^%((.-)%)%s*%((.-)%)$") + if not p1 then + return + end -local function test_string_to_area() - local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2, 4, -12.53)") - assert(p1.x == 10.0 and p1.y == 5 and p1.z == -2) - assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53) + p1 = parse_area_string(p1, relative_to) + p2 = parse_area_string(p2, relative_to) - p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53") - assert(p1 == nil and p2 == nil) + if p1 == nil or p2 == nil then + return + end - p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53") - assert(p1 == nil and p2 == nil) + return p1, p2 + end end -test_string_to_area() - -------------------------------------------------------------------------------- function table.copy(t, seen) local n = {} @@ -701,3 +700,71 @@ end function core.is_nan(number) return number ~= number end + +--[[ Helper function for parsing an optionally relative number +of a chat command parameter, using the chat command tilde notation. + +Parameters: +* arg: String snippet containing the number; possible values: + * "<number>": return as number + * "~<number>": return relative_to + <number> + * "~": return relative_to + * Anything else will return `nil` +* relative_to: Number to which the `arg` number might be relative to + +Returns: +A number or `nil`, depending on `arg. + +Examples: +* `core.parse_relative_number("5", 10)` returns 5 +* `core.parse_relative_number("~5", 10)` returns 15 +* `core.parse_relative_number("~", 10)` returns 10 +]] +function core.parse_relative_number(arg, relative_to) + if not arg then + return nil + elseif arg == "~" then + return relative_to + elseif string.sub(arg, 1, 1) == "~" then + local number = tonumber(string.sub(arg, 2)) + if not number then + return nil + end + if core.is_nan(number) or number == math.huge or number == -math.huge then + return nil + end + return relative_to + number + else + local number = tonumber(arg) + if core.is_nan(number) or number == math.huge or number == -math.huge then + return nil + end + return number + end +end + +--[[ Helper function to parse coordinates that might be relative +to another position; supports chat command tilde notation. +Intended to be used in chat command parameter parsing. + +Parameters: +* x, y, z: Parsed x, y, and z coordinates as strings +* relative_to: Position to which to compare the position + +Syntax of x, y and z: +* "<number>": return as number +* "~<number>": return <number> + player position on this axis +* "~": return player position on this axis + +Returns: a vector or nil for invalid input or if player does not exist +]] +function core.parse_coordinates(x, y, z, relative_to) + if not relative_to then + x, y, z = tonumber(x), tonumber(y), tonumber(z) + return x and y and z and { x = x, y = y, z = z } + end + local rx = core.parse_relative_number(x, relative_to.x) + local ry = core.parse_relative_number(y, relative_to.y) + local rz = core.parse_relative_number(z, relative_to.z) + return rx and ry and rz and { x = rx, y = ry, z = rz } +end diff --git a/builtin/common/mod_storage.lua b/builtin/common/mod_storage.lua new file mode 100644 index 000000000..7ccf62900 --- /dev/null +++ b/builtin/common/mod_storage.lua @@ -0,0 +1,19 @@ +-- Modify core.get_mod_storage to return the storage for the current mod. + +local get_current_modname = core.get_current_modname + +local old_get_mod_storage = core.get_mod_storage + +local storages = setmetatable({}, { + __mode = "v", -- values are weak references (can be garbage-collected) + __index = function(self, modname) + local storage = old_get_mod_storage(modname) + self[modname] = storage + return storage + end, +}) + +function core.get_mod_storage() + local modname = get_current_modname() + return modname and storages[modname] +end diff --git a/builtin/common/serialize.lua b/builtin/common/serialize.lua index 300b394c6..caf989e69 100644 --- a/builtin/common/serialize.lua +++ b/builtin/common/serialize.lua @@ -1,205 +1,224 @@ --- Lua module to serialize values as Lua code. --- From: https://github.com/fab13n/metalua/blob/no-dll/src/lib/serialize.lua +-- From: https://github.com/appgurueu/modlib/blob/master/luon.lua -- License: MIT --- @copyright 2006-2997 Fabien Fleutot <metalua@gmail.com> --- @author Fabien Fleutot <metalua@gmail.com> --- @author ShadowNinja <shadowninja@minetest.net> --------------------------------------------------------------------------------- ---- Serialize an object into a source code string. This string, when passed as --- an argument to deserialize(), returns an object structurally identical to --- the original one. The following are currently supported: --- * Booleans, numbers, strings, and nil. --- * Functions; uses interpreter-dependent (and sometimes platform-dependent) bytecode! --- * Tables; they can cantain multiple references and can be recursive, but metatables aren't saved. --- This works in two phases: --- 1. Recursively find and record multiple references and recursion. --- 2. Recursively dump the value into a string. --- @param x Value to serialize (nil is allowed). --- @return load()able string containing the value. -function core.serialize(x) - local local_index = 1 -- Top index of the "_" local table in the dump - -- table->nil/1/2 set of tables seen. - -- nil = not seen, 1 = seen once, 2 = seen multiple times. - local seen = {} +local next, rawget, pairs, pcall, error, type, setfenv, loadstring + = next, rawget, pairs, pcall, error, type, setfenv, loadstring - -- nest_points are places where a table appears within itself, directly - -- or not. For instance, all of these chunks create nest points in - -- table x: "x = {}; x[x] = 1", "x = {}; x[1] = x", - -- "x = {}; x[1] = {y = {x}}". - -- To handle those, two tables are used by mark_nest_point: - -- * nested - Transient set of tables being currently traversed. - -- Used for detecting nested tables. - -- * nest_points - parent->{key=value, ...} table cantaining the nested - -- keys and values in the parent. They're all dumped after all the - -- other table operations have been performed. - -- - -- mark_nest_point(p, k, v) fills nest_points with information required - -- to remember that key/value (k, v) creates a nest point in table - -- parent. It also marks "parent" and the nested item(s) as occuring - -- multiple times, since several references to it will be required in - -- order to patch the nest points. - local nest_points = {} - local nested = {} - local function mark_nest_point(parent, k, v) - local nk, nv = nested[k], nested[v] - local np = nest_points[parent] - if not np then - np = {} - nest_points[parent] = np - end - np[k] = v - seen[parent] = 2 - if nk then seen[k] = 2 end - if nv then seen[v] = 2 end - end +local table_concat, string_dump, string_format, string_match, math_huge + = table.concat, string.dump, string.format, string.match, math.huge - -- First phase, list the tables and functions which appear more than - -- once in x. - local function mark_multiple_occurences(x) - local tp = type(x) - if tp ~= "table" and tp ~= "function" then - -- No identity (comparison is done by value, not by instance) +-- Recursively counts occurences of objects (non-primitives including strings) in a table. +local function count_objects(value) + local counts = {} + if value == nil then + -- Early return for nil; tables can't contain nil + return counts + end + local function count_values(val) + local type_ = type(val) + if type_ == "boolean" or type_ == "number" then return end - if seen[x] == 1 then - seen[x] = 2 - elseif seen[x] ~= 2 then - seen[x] = 1 - end - - if tp == "table" then - nested[x] = true - for k, v in pairs(x) do - if nested[k] or nested[v] then - mark_nest_point(x, k, v) - else - mark_multiple_occurences(k) - mark_multiple_occurences(v) + local count = counts[val] + counts[val] = (count or 0) + 1 + if type_ == "table" then + if not count then + for k, v in pairs(val) do + count_values(k) + count_values(v) end end - nested[x] = nil + elseif type_ ~= "string" and type_ ~= "function" then + error("unsupported type: " .. type_) end end + count_values(value) + return counts +end - local dumped = {} -- object->varname set - local local_defs = {} -- Dumped local definitions as source code lines +-- Build a "set" of Lua keywords. These can't be used as short key names. +-- See https://www.lua.org/manual/5.1/manual.html#2.1 +local keywords = {} +for _, keyword in pairs({ + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "if", + "in", "local", "nil", "not", "or", + "repeat", "return", "then", "true", "until", "while", + "goto" -- LuaJIT, Lua 5.2+ +}) do + keywords[keyword] = true +end - -- Mutually recursive local functions: - local dump_val, dump_or_ref_val +local function quote(string) + return string_format("%q", string) +end - -- If x occurs multiple times, dump the local variable rather than - -- the value. If it's the first time it's dumped, also dump the - -- content in local_defs. - function dump_or_ref_val(x) - if seen[x] ~= 2 then - return dump_val(x) - end - local var = dumped[x] - if var then -- Already referenced - return var +local function dump_func(func) + return string_format("loadstring(%q)", string_dump(func)) +end + +-- Serializes Lua nil, booleans, numbers, strings, tables and even functions +-- Tables are referenced by reference, strings are referenced by value. Supports circular tables. +local function serialize(value, write) + local reference, refnum = "r1", 1 + -- [object] = reference string + local references = {} + -- Circular tables that must be filled using `table[key] = value` statements + local to_fill = {} + for object, count in pairs(count_objects(value)) do + local type_ = type(object) + -- Object must appear more than once. If it is a string, the reference has to be shorter than the string. + if count >= 2 and (type_ ~= "string" or #reference + 2 < #object) then + write(reference) + write("=") + if type_ == "table" then + write("{}") + elseif type_ == "function" then + write(dump_func(object)) + elseif type_ == "string" then + write(quote(object)) + end + write(";") + references[object] = reference + if type_ == "table" then + to_fill[object] = reference + end + refnum = refnum + 1 + reference = ("r%X"):format(refnum) end - -- First occurence, create and register reference - local val = dump_val(x) - local i = local_index - local_index = local_index + 1 - var = "_["..i.."]" - local_defs[#local_defs + 1] = var.." = "..val - dumped[x] = var - return var end - - -- Second phase. Dump the object; subparts occuring multiple times - -- are dumped in local variables which can be referenced multiple - -- times. Care is taken to dump local vars in a sensible order. - function dump_val(x) - local tp = type(x) - if x == nil then return "nil" - elseif tp == "string" then return string.format("%q", x) - elseif tp == "boolean" then return x and "true" or "false" - elseif tp == "function" then - return string.format("loadstring(%q)", string.dump(x)) - elseif tp == "number" then - -- Serialize numbers reversibly with string.format - return string.format("%.17g", x) - elseif tp == "table" then - local vals = {} - local idx_dumped = {} - local np = nest_points[x] - for i, v in ipairs(x) do - if not np or not np[i] then - vals[#vals + 1] = dump_or_ref_val(v) - end - idx_dumped[i] = true + -- Used to decide whether we should do "key=..." + local function use_short_key(key) + return not references[key] and type(key) == "string" and (not keywords[key]) and string_match(key, "^[%a_][%a%d_]*$") + end + local function dump(value) + -- Primitive types + if value == nil then + return write("nil") + end + if value == true then + return write("true") + end + if value == false then + return write("false") + end + local type_ = type(value) + if type_ == "number" then + return write(string_format("%.17g", value)) + end + -- Reference types: table, function and string + local ref = references[value] + if ref then + return write(ref) + end + if type_ == "string" then + return write(quote(value)) + end + if type_ == "function" then + return write(dump_func(value)) + end + if type_ == "table" then + write("{") + -- First write list keys: + -- Don't use the table length #value here as it may horribly fail + -- for tables which use large integers as keys in the hash part; + -- stop at the first "hole" (nil value) instead + local len = 0 + local first = true -- whether this is the first entry, which may not have a leading comma + while true do + local v = rawget(value, len + 1) -- use rawget to avoid metatables like the vector metatable + if v == nil then break end + if first then first = false else write(",") end + dump(v) + len = len + 1 end - for k, v in pairs(x) do - if (not np or not np[k]) and - not idx_dumped[k] then - vals[#vals + 1] = "["..dump_or_ref_val(k).."] = " - ..dump_or_ref_val(v) + -- Now write map keys ([key] = value) + for k, v in next, value do + -- We have written all non-float keys in [1, len] already + if type(k) ~= "number" or k % 1 ~= 0 or k < 1 or k > len then + if first then first = false else write(",") end + if use_short_key(k) then + write(k) + else + write("[") + dump(k) + write("]") + end + write("=") + dump(v) end end - return "{"..table.concat(vals, ", ").."}" - else - error("Can't serialize data of type "..tp) + write("}") + return end end - - local function dump_nest_points() - for parent, vals in pairs(nest_points) do - for k, v in pairs(vals) do - local_defs[#local_defs + 1] = dump_or_ref_val(parent) - .."["..dump_or_ref_val(k).."] = " - ..dump_or_ref_val(v) + -- Write the statements to fill circular tables + for table, ref in pairs(to_fill) do + for k, v in pairs(table) do + write(ref) + if use_short_key(k) then + write(".") + write(k) + else + write("[") + dump(k) + write("]") end + write("=") + dump(v) + write(";") end end - - mark_multiple_occurences(x) - local top_level = dump_or_ref_val(x) - dump_nest_points() - - if next(local_defs) then - return "local _ = {}\n" - ..table.concat(local_defs, "\n") - .."\nreturn "..top_level - else - return "return "..top_level - end + write("return ") + dump(value) end --- Deserialization - -local function safe_loadstring(...) - local func, err = loadstring(...) - if func then - setfenv(func, {}) - return func - end - return nil, err +function core.serialize(value) + local rope = {} + serialize(value, function(text) + -- Faster than table.insert(rope, text) on PUC Lua 5.1 + rope[#rope + 1] = text + end) + return table_concat(rope) end local function dummy_func() end +local nan = (0/0)^1 -- +nan + function core.deserialize(str, safe) - if type(str) ~= "string" then - return nil, "Cannot deserialize type '"..type(str) - .."'. Argument must be a string." + -- Backwards compatibility + if str == nil then + core.log("deprecated", "minetest.deserialize called with nil (expected string).") + return nil, "Invalid type: Expected a string, got nil" end - if str:byte(1) == 0x1B then - return nil, "Bytecode prohibited" + local t = type(str) + if t ~= "string" then + error(("minetest.deserialize called with %s (expected string)."):format(t)) end - local f, err = loadstring(str) - if not f then return nil, err end - -- The environment is recreated every time so deseralized code cannot - -- pollute it with permanent references. - setfenv(f, {loadstring = safe and dummy_func or safe_loadstring}) + local func, err = loadstring(str) + if not func then return nil, err end - local good, data = pcall(f) - if good then - return data + -- math.huge is serialized to inf, NaNs are serialized to nan by Lua + local env = {inf = math_huge, nan = nan} + if safe then + env.loadstring = dummy_func else - return nil, data + env.loadstring = function(str, ...) + local func, err = loadstring(str, ...) + if func then + setfenv(func, env) + return func + end + return nil, err + end + end + setfenv(func, env) + local success, value_or_err = pcall(func) + if success then + return value_or_err end + return nil, value_or_err end diff --git a/builtin/common/strict.lua b/builtin/common/strict.lua index ccde9676b..936ebb37b 100644 --- a/builtin/common/strict.lua +++ b/builtin/common/strict.lua @@ -1,9 +1,4 @@ - --- Always warn when creating a global variable, even outside of a function. --- This ignores mod namespaces (variables with the same name as the current mod). -local WARN_INIT = false - -local getinfo = debug.getinfo +local getinfo, rawget, rawset = debug.getinfo, rawget, rawset function core.global_exists(name) if type(name) ~= "string" then @@ -19,39 +14,33 @@ local declared = {} local warned = {} function meta:__newindex(name, value) + if declared[name] then + return + end local info = getinfo(2, "Sl") local desc = ("%s:%d"):format(info.short_src, info.currentline) - if not declared[name] then - local warn_key = ("%s\0%d\0%s"):format(info.source, - info.currentline, name) - if not warned[warn_key] and info.what ~= "main" and - info.what ~= "C" then - core.log("warning", ("Assignment to undeclared ".. - "global %q inside a function at %s.") + local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name) + if not warned[warn_key] and info.what ~= "main" and info.what ~= "C" then + core.log("warning", ("Assignment to undeclared global %q inside a function at %s.") :format(name, desc)) - warned[warn_key] = true - end - declared[name] = true - end - -- Ignore mod namespaces - if WARN_INIT and name ~= core.get_current_modname() then - core.log("warning", ("Global variable %q created at %s.") - :format(name, desc)) + warned[warn_key] = true end rawset(self, name, value) + declared[name] = true end function meta:__index(name) + if declared[name] then + return + end local info = getinfo(2, "Sl") local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name) - if not declared[name] and not warned[warn_key] and info.what ~= "C" then + if not warned[warn_key] and info.what ~= "C" then core.log("warning", ("Undeclared global variable %q accessed at %s:%s") :format(name, info.short_src, info.currentline)) warned[warn_key] = true end - return rawget(self, name) end setmetatable(_G, meta) - diff --git a/builtin/common/tests/misc_helpers_spec.lua b/builtin/common/tests/misc_helpers_spec.lua index b16987f0b..7d046d5b7 100644 --- a/builtin/common/tests/misc_helpers_spec.lua +++ b/builtin/common/tests/misc_helpers_spec.lua @@ -1,4 +1,5 @@ _G.core = {} +_G.vector = {metatable = {}} dofile("builtin/common/vector.lua") dofile("builtin/common/misc_helpers.lua") @@ -66,9 +67,107 @@ describe("pos", function() end) end) +describe("area parsing", function() + describe("valid inputs", function() + it("accepts absolute numbers", function() + local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2 4 -12.53)") + assert(p1.x == 10 and p1.y == 5 and p1.z == -2) + assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53) + end) + + it("accepts relative numbers", function() + local p1, p2 = core.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10}) + assert(type(p1) == "table" and type(p2) == "table") + assert(p1.x == 1 and p1.y == 2 and p1.z == 3) + assert(p2.x == 15 and p2.y == 5 and p2.z == 10) + + p1, p2 = core.string_to_area("(1 2 3) (~5 ~-5 ~)", {x=10,y=10,z=10}) + assert(type(p1) == "table" and type(p2) == "table") + assert(p1.x == 1 and p1.y == 2 and p1.z == 3) + assert(p2.x == 15 and p2.y == 5 and p2.z == 10) + end) + end) + describe("invalid inputs", function() + it("rejects too few numbers", function() + local p1, p2 = core.string_to_area("(1,1) (1,1,1,1)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + end) + + it("rejects too many numbers", function() + local p1, p2 = core.string_to_area("(1,1,1,1) (1,1,1,1)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + end) + + it("rejects nan & inf", function() + local p1, p2 = core.string_to_area("(1,1,1) (1,1,nan)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(1,1,1) (1,1,~nan)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(1,1,1) (1,~nan,1)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(1,1,1) (1,1,inf)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(1,1,1) (1,1,~inf)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(1,1,1) (1,~inf,1)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)") + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)") + assert(p1 == nil and p2 == nil) + end) + + it("rejects words", function() + local p1, p2 = core.string_to_area("bananas", {x=1,y=1,z=1}) + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("bananas", "foobar") + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("bananas") + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(bananas,bananas,bananas)") + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(bananas,bananas,bananas) (bananas,bananas,bananas)") + assert(p1 == nil and p2 == nil) + end) + + it("requires parenthesis & valid numbers", function() + local p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53") + assert(p1 == nil and p2 == nil) + + p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53") + assert(p1 == nil and p2 == nil) + end) + end) +end) + describe("table", function() it("indexof()", function() assert.equal(1, table.indexof({"foo", "bar"}, "foo")) assert.equal(-1, table.indexof({"foo", "bar"}, "baz")) end) end) + +describe("formspec_escape", function() + it("escapes", function() + assert.equal(nil, core.formspec_escape(nil)) + assert.equal("", core.formspec_escape("")) + assert.equal("\\[Hello\\\\\\[", core.formspec_escape("[Hello\\[")) + end) +end) diff --git a/builtin/common/tests/serialize_spec.lua b/builtin/common/tests/serialize_spec.lua index e46b7dcc5..340e226ee 100644 --- a/builtin/common/tests/serialize_spec.lua +++ b/builtin/common/tests/serialize_spec.lua @@ -1,42 +1,97 @@ _G.core = {} +_G.vector = {metatable = {}} _G.setfenv = require 'busted.compatibility'.setfenv dofile("builtin/common/serialize.lua") dofile("builtin/common/vector.lua") +-- Supports circular tables; does not support table keys +-- Correctly checks whether a mapping of references ("same") exists +-- Is significantly more efficient than assert.same +local function assert_same(a, b, same) + same = same or {} + if same[a] or same[b] then + assert(same[a] == b and same[b] == a) + return + end + if a == b then + return + end + if type(a) ~= "table" or type(b) ~= "table" then + assert(a == b) + return + end + same[a] = b + same[b] = a + local count = 0 + for k, v in pairs(a) do + count = count + 1 + assert(type(k) ~= "table") + assert_same(v, b[k], same) + end + for _ in pairs(b) do + count = count - 1 + end + assert(count == 0) +end + +local x, y = {}, {} +local t1, t2 = {x, x, y, y}, {x, y, x, y} +assert.same(t1, t2) -- will succeed because it only checks whether the depths match +assert(not pcall(assert_same, t1, t2)) -- will correctly fail because it checks whether the refs match + describe("serialize", function() + local function assert_preserves(value) + local preserved_value = core.deserialize(core.serialize(value)) + assert_same(value, preserved_value) + end it("works", function() - local test_in = {cat={sound="nyan", speed=400}, dog={sound="woof"}} - local test_out = core.deserialize(core.serialize(test_in)) - - assert.same(test_in, test_out) + assert_preserves({cat={sound="nyan", speed=400}, dog={sound="woof"}}) end) it("handles characters", function() - local test_in = {escape_chars="\n\r\t\v\\\"\'", non_european="θשׁ٩∂"} - local test_out = core.deserialize(core.serialize(test_in)) - assert.same(test_in, test_out) + assert_preserves({escape_chars="\n\r\t\v\\\"\'", non_european="θשׁ٩∂"}) + end) + + it("handles NaN & infinities", function() + local nan = core.deserialize(core.serialize(0/0)) + assert(nan ~= nan) + assert_preserves(math.huge) + assert_preserves(-math.huge) end) it("handles precise numbers", function() - local test_in = 0.2695949158945771 - local test_out = core.deserialize(core.serialize(test_in)) - assert.same(test_in, test_out) + assert_preserves(0.2695949158945771) end) it("handles big integers", function() - local test_in = 269594915894577 - local test_out = core.deserialize(core.serialize(test_in)) - assert.same(test_in, test_out) + assert_preserves(269594915894577) end) it("handles recursive structures", function() local test_in = { hello = "world" } test_in.foo = test_in + assert_preserves(test_in) + end) + + it("handles cross-referencing structures", function() + local test_in = { + foo = { + baz = { + {} + }, + }, + bar = { + baz = {}, + }, + } - local test_out = core.deserialize(core.serialize(test_in)) - assert.same(test_in, test_out) + test_in.foo.baz[1].foo = test_in.foo + test_in.foo.baz[1].bar = test_in.bar + test_in.bar.baz[1] = test_in.foo.baz[1] + + assert_preserves(test_in) end) it("strips functions in safe mode", function() @@ -46,6 +101,7 @@ describe("serialize", function() end, foo = "bar" } + setfenv(test_in.func, _G) local str = core.serialize(test_in) assert.not_nil(str:find("loadstring")) @@ -57,13 +113,77 @@ describe("serialize", function() it("vectors work", function() local v = vector.new(1, 2, 3) - assert.same({{x = 1, y = 2, z = 3}}, core.deserialize(core.serialize({v}))) - assert.same({x = 1, y = 2, z = 3}, core.deserialize(core.serialize(v))) + assert_preserves({v}) + assert_preserves(v) -- abuse v = vector.new(1, 2, 3) v.a = "bla" - assert.same({x = 1, y = 2, z = 3, a = "bla"}, - core.deserialize(core.serialize(v))) + assert_preserves(v) + end) + + it("handles keywords as keys", function() + assert_preserves({["and"] = "keyword", ["for"] = "keyword"}) + end) + + describe("fuzzing", function() + local atomics = {true, false, math.huge, -math.huge} -- no NaN or nil + local function atomic() + return atomics[math.random(1, #atomics)] + end + local function num() + local sign = math.random() < 0.5 and -1 or 1 + -- HACK math.random(a, b) requires a, b & b - a to fit within a 32-bit int + -- Use two random calls to generate a random number from 0 - 2^50 as lower & upper 25 bits + local val = math.random(0, 2^25) * 2^25 + math.random(0, 2^25 - 1) + local exp = math.random() < 0.5 and 1 or 2^(math.random(-120, 120)) + return sign * val * exp + end + local function charcodes(count) + if count == 0 then return end + return math.random(0, 0xFF), charcodes(count - 1) + end + local function str() + return string.char(charcodes(math.random(0, 100))) + end + local primitives = {atomic, num, str} + local function primitive() + return primitives[math.random(1, #primitives)]() + end + local function tab(max_actions) + local root = {} + local tables = {root} + local function random_table() + return tables[math.random(1, #tables)] + end + for _ = 1, math.random(1, max_actions) do + local tab = random_table() + local value + if math.random() < 0.5 then + if math.random() < 0.5 then + value = random_table() + else + value = {} + table.insert(tables, value) + end + else + value = primitive() + end + tab[math.random() < 0.5 and (#tab + 1) or primitive()] = value + end + return root + end + it("primitives work", function() + for _ = 1, 1e3 do + assert_preserves(primitive()) + end + end) + it("tables work", function() + for _ = 1, 100 do + local fuzzed_table = tab(1e3) + assert_same(fuzzed_table, table.copy(fuzzed_table)) + assert_preserves(fuzzed_table) + end + end) end) end) diff --git a/builtin/common/tests/vector_spec.lua b/builtin/common/tests/vector_spec.lua index 2f72f3383..6a0b81a89 100644 --- a/builtin/common/tests/vector_spec.lua +++ b/builtin/common/tests/vector_spec.lua @@ -1,4 +1,4 @@ -_G.vector = {} +_G.vector = {metatable = {}} dofile("builtin/common/vector.lua") describe("vector", function() @@ -128,6 +128,14 @@ describe("vector", function() assert.equal(vector.new(4.1, 5.9, 5.5), a:apply(f)) end) + it("combine()", function() + local a = vector.new(1, 2, 3) + local b = vector.new(3, 2, 1) + assert.equal(vector.add(a, b), vector.combine(a, b, function(x, y) return x + y end)) + assert.equal(vector.new(3, 2, 3), vector.combine(a, b, math.max)) + assert.equal(vector.new(1, 2, 1), vector.combine(a, b, math.min)) + end) + it("equals()", function() local function assertE(a, b) assert.is_true(vector.equals(a, b)) diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua index 581d014e0..a08472e32 100644 --- a/builtin/common/vector.lua +++ b/builtin/common/vector.lua @@ -6,10 +6,8 @@ Note: The vector.*-functions must be able to accept old vectors that had no meta -- localize functions local setmetatable = setmetatable -vector = {} - -local metatable = {} -vector.metatable = metatable +-- vector.metatable is set by C++. +local metatable = vector.metatable local xyz = {"x", "y", "z"} @@ -112,6 +110,14 @@ function vector.apply(v, func) ) end +function vector.combine(a, b, func) + return fast_new( + func(a.x, b.x), + func(a.y, b.y), + func(a.z, b.z) + ) +end + function vector.distance(a, b) local x = a.x - b.x local y = a.y - b.y diff --git a/builtin/game/async.lua b/builtin/game/async.lua new file mode 100644 index 000000000..469f179d7 --- /dev/null +++ b/builtin/game/async.lua @@ -0,0 +1,22 @@ + +core.async_jobs = {} + +function core.async_event_handler(jobid, retval) + local callback = core.async_jobs[jobid] + assert(type(callback) == "function") + callback(unpack(retval, 1, retval.n)) + core.async_jobs[jobid] = nil +end + +function core.handle_async(func, callback, ...) + assert(type(func) == "function" and type(callback) == "function", + "Invalid minetest.handle_async invocation") + local args = {n = select("#", ...), ...} + local mod_origin = core.get_last_run_mod() + + local jobid = core.do_async_callback(func, args, mod_origin) + core.async_jobs[jobid] = callback + + return true +end + diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua index 493bb92c0..bbcdcf2d0 100644 --- a/builtin/game/chat.lua +++ b/builtin/game/chat.lua @@ -130,8 +130,13 @@ local function parse_range_str(player_name, str) return false, S("Unable to get position of player @1.", player_name) end else - p1, p2 = core.string_to_area(str) - if p1 == nil then + local player = core.get_player_by_name(player_name) + local relpos + if player then + relpos = player:get_pos() + end + p1, p2 = core.string_to_area(str, relpos) + if p1 == nil or p2 == nil then return false, S("Incorrect area format. " .. "Expected: (x1,y1,z1) (x2,y2,z2)") end @@ -310,12 +315,7 @@ local function handle_revoke_command(caller, revokename, revokeprivstr) and revokename == core.settings:get("name") and revokename ~= "" if revokeprivstr == "all" then - revokeprivs = privs - privs = {} - else - for priv, _ in pairs(revokeprivs) do - privs[priv] = nil - end + revokeprivs = table.copy(privs) end local privs_unknown = "" @@ -332,7 +332,10 @@ local function handle_revoke_command(caller, revokename, revokeprivstr) end local def = core.registered_privileges[priv] if not def then - privs_unknown = privs_unknown .. S("Unknown privilege: @1", priv) .. "\n" + -- Old/removed privileges might still be granted to certain players + if not privs[priv] then + privs_unknown = privs_unknown .. S("Unknown privilege: @1", priv) .. "\n" + end elseif is_singleplayer and def.give_to_singleplayer then irrevokable[priv] = true elseif is_admin and def.give_to_admin then @@ -359,19 +362,22 @@ local function handle_revoke_command(caller, revokename, revokeprivstr) end local revokecount = 0 - - core.set_player_privs(revokename, privs) for priv, _ in pairs(revokeprivs) do - -- call the on_revoke callbacks - core.run_priv_callbacks(revokename, priv, caller, "revoke") + privs[priv] = nil revokecount = revokecount + 1 end - local new_privs = core.get_player_privs(revokename) if revokecount == 0 then return false, S("No privileges were revoked.") end + core.set_player_privs(revokename, privs) + for priv, _ in pairs(revokeprivs) do + -- call the on_revoke callbacks + core.run_priv_callbacks(revokename, priv, caller, "revoke") + end + local new_privs = core.get_player_privs(revokename) + core.log("action", caller..' revoked (' ..core.privs_to_string(revokeprivs, ', ') ..') privileges from '..revokename) @@ -569,10 +575,15 @@ core.register_chatcommand("teleport", { description = S("Teleport to position or player"), privs = {teleport=true}, func = function(name, param) + local player = core.get_player_by_name(name) + local relpos + if player then + relpos = player:get_pos() + end local p = {} - p.x, p.y, p.z = param:match("^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") - p = vector.apply(p, tonumber) - if p.x and p.y and p.z then + p.x, p.y, p.z = string.match(param, "^([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$") + p = core.parse_coordinates(p.x, p.y, p.z, relpos) + if p and p.x and p.y and p.z then return teleport_to_pos(name, p) end @@ -586,9 +597,19 @@ core.register_chatcommand("teleport", { "other players (missing privilege: @1).", "bring") local teleportee_name + p = {} teleportee_name, p.x, p.y, p.z = param:match( - "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") + "^([^ ]+) +([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$") + if teleportee_name then + local teleportee = core.get_player_by_name(teleportee_name) + if not teleportee then + return + end + relpos = teleportee:get_pos() + p = core.parse_coordinates(p.x, p.y, p.z, relpos) + end p = vector.apply(p, tonumber) + if teleportee_name and p.x and p.y and p.z then if not has_bring_priv then return false, missing_bring_msg @@ -621,6 +642,10 @@ core.register_chatcommand("set", { setname, setvalue = string.match(param, "([^ ]+) (.+)") if setname and setvalue then + if setname:sub(1, 7) == "secure." then + return false, S("Failed. Cannot modify secure settings. " + .. "Edit the settings file manually.") + end if not core.settings:get(setname) then return false, S("Failed. Use '/set -n <name> <value>' " .. "to create a new setting.") @@ -837,7 +862,7 @@ core.register_chatcommand("spawnentity", { description = S("Spawn entity at given (or your) position"), privs = {give=true, interact=true}, func = function(name, param) - local entityname, p = string.match(param, "^([^ ]+) *(.*)$") + local entityname, pstr = string.match(param, "^([^ ]+) *(.*)$") if not entityname then return false, S("EntityName required.") end @@ -851,11 +876,15 @@ core.register_chatcommand("spawnentity", { if not core.registered_entities[entityname] then return false, S("Cannot spawn an unknown entity.") end - if p == "" then + local p + if pstr == "" then p = player:get_pos() else - p = core.string_to_pos(p) - if p == nil then + p = {} + p.x, p.y, p.z = string.match(pstr, "^([%d.~-]+)[, ] *([%d.~-]+)[, ] *([%d.~-]+)$") + local relpos = player:get_pos() + p = core.parse_coordinates(p.x, p.y, p.z, relpos) + if not (p and p.x and p.y and p.z) then return false, S("Invalid parameters (@1).", param) end end @@ -1014,6 +1043,13 @@ core.register_chatcommand("status", { end, }) +local function get_time(timeofday) + local time = math.floor(timeofday * 1440) + local minute = time % 60 + local hour = (time - minute) / 60 + return time, hour, minute +end + core.register_chatcommand("time", { params = S("[<0..23>:<0..59> | <0..24000>]"), description = S("Show or set time of day"), @@ -1032,9 +1068,14 @@ core.register_chatcommand("time", { return false, S("You don't have permission to run " .. "this command (missing privilege: @1).", "settime") end - local hour, minute = param:match("^(%d+):(%d+)$") - if not hour then - local new_time = tonumber(param) or -1 + local relative, negative, hour, minute = param:match("^(~?)(%-?)(%d+):(%d+)$") + if not relative then -- checking the first capture against nil suffices + local new_time = core.parse_relative_number(param, core.get_timeofday() * 24000) + if not new_time then + new_time = tonumber(param) or -1 + else + new_time = new_time % 24000 + end if new_time ~= new_time or new_time < 0 or new_time > 24000 then return false, S("Invalid time (must be between 0 and 24000).") end @@ -1042,14 +1083,29 @@ core.register_chatcommand("time", { core.log("action", name .. " sets time to " .. new_time) return true, S("Time of day changed.") end + local new_time hour = tonumber(hour) minute = tonumber(minute) - if hour < 0 or hour > 23 then - return false, S("Invalid hour (must be between 0 and 23 inclusive).") - elseif minute < 0 or minute > 59 then - return false, S("Invalid minute (must be between 0 and 59 inclusive).") + if relative == "" then + if hour < 0 or hour > 23 then + return false, S("Invalid hour (must be between 0 and 23 inclusive).") + elseif minute < 0 or minute > 59 then + return false, S("Invalid minute (must be between 0 and 59 inclusive).") + end + new_time = (hour * 60 + minute) / 1440 + else + if minute < 0 or minute > 59 then + return false, S("Invalid minute (must be between 0 and 59 inclusive).") + end + local current_time = core.get_timeofday() + if negative == "-" then -- negative time + hour, minute = -hour, -minute + end + new_time = (current_time + (hour * 60 + minute) / 1440) % 1 + local _ + _, hour, minute = get_time(new_time) end - core.set_timeofday((hour * 60 + minute) / 1440) + core.set_timeofday(new_time) core.log("action", ("%s sets time to %d:%02d"):format(name, hour, minute)) return true, S("Time of day changed.") end, @@ -1131,6 +1187,9 @@ core.register_chatcommand("ban", { return true, S("Ban list: @1", ban_list) end end + if core.is_singleplayer() then + return false, S("You cannot ban players in singleplayer!") + end if not core.get_player_by_name(param) then return false, S("Player is not online.") end diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 29cb56aae..d5727f2a7 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -158,12 +158,10 @@ core.register_entity(":__builtin:falling_node", { or def.drawtype == "normal" or def.drawtype == "nodebox" then if (def.paramtype2 == "facedir" or def.paramtype2 == "colorfacedir") then - local fdir = node.param2 % 32 + local fdir = node.param2 % 32 % 24 -- Get rotation from a precalculated lookup table local euler = facedir_to_euler[fdir + 1] - if euler then - self.object:set_rotation(euler) - end + self.object:set_rotation(euler) elseif (def.drawtype ~= "plantlike" and def.drawtype ~= "plantlike_rooted" and (def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted" or def.drawtype == "signlike")) then local rot = node.param2 % 8 @@ -264,7 +262,7 @@ core.register_entity(":__builtin:falling_node", { end -- Decide if we're replacing the node or placing on top - local np = vector.new(bcp) + local np = vector.copy(bcp) if bcd and bcd.buildable_to and (not self.floats or bcd.liquidtype == "none") then core.remove_node(bcp) @@ -436,7 +434,7 @@ local function drop_attached_node(p) if def and def.preserve_metadata then local oldmeta = core.get_meta(p):to_table().fields -- Copy pos and node because the callback can modify them. - local pos_copy = vector.new(p) + local pos_copy = vector.copy(p) local node_copy = {name=n.name, param1=n.param1, param2=n.param2} local drop_stacks = {} for k, v in pairs(drops) do @@ -461,7 +459,7 @@ end function builtin_shared.check_attached_node(p, n) local def = core.registered_nodes[n.name] - local d = vector.new() + local d = vector.zero() if def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted" then -- The fallback vector here is in case 'wallmounted to dir' is nil due diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 583ef5092..73b16361e 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -21,7 +21,9 @@ core.features = { use_texture_alpha_string_modes = true, degrotate_240_steps = true, abm_min_max_y = true, + particlespawner_tweenable = true, dynamic_add_media_table = true, + get_sky_as_table = true, } function core.has_feature(arg) diff --git a/builtin/game/init.lua b/builtin/game/init.lua index bb007fabd..d7606f357 100644 --- a/builtin/game/init.lua +++ b/builtin/game/init.lua @@ -8,6 +8,7 @@ local gamepath = scriptpath .. "game".. DIR_DELIM local builtin_shared = {} dofile(gamepath .. "constants.lua") +dofile(gamepath .. "item_s.lua") assert(loadfile(gamepath .. "item.lua"))(builtin_shared) dofile(gamepath .. "register.lua") @@ -16,8 +17,10 @@ if core.settings:get_bool("profiler.load") then end dofile(commonpath .. "after.lua") +dofile(commonpath .. "mod_storage.lua") dofile(gamepath .. "item_entity.lua") dofile(gamepath .. "deprecated.lua") +dofile(gamepath .. "misc_s.lua") dofile(gamepath .. "misc.lua") dofile(gamepath .. "privileges.lua") dofile(gamepath .. "auth.lua") @@ -32,5 +35,6 @@ dofile(gamepath .. "voxelarea.lua") dofile(gamepath .. "forceloading.lua") dofile(gamepath .. "statbars.lua") dofile(gamepath .. "knockback.lua") +dofile(gamepath .. "async.lua") profiler = nil diff --git a/builtin/game/item.lua b/builtin/game/item.lua index 2a4b4e38f..b49787987 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -5,8 +5,8 @@ local builtin_shared = ... local function copy_pointed_thing(pointed_thing) return { type = pointed_thing.type, - above = vector.new(pointed_thing.above), - under = vector.new(pointed_thing.under), + above = pointed_thing.above and vector.copy(pointed_thing.above), + under = pointed_thing.under and vector.copy(pointed_thing.under), ref = pointed_thing.ref, } end @@ -15,15 +15,6 @@ end -- Item definition helpers -- -function core.inventorycube(img1, img2, img3) - img2 = img2 or img1 - img3 = img3 or img1 - return "[inventorycube" - .. "{" .. img1:gsub("%^", "&") - .. "{" .. img2:gsub("%^", "&") - .. "{" .. img3:gsub("%^", "&") -end - function core.get_pointed_thing_position(pointed_thing, above) if pointed_thing.type == "node" then if above then @@ -37,144 +28,6 @@ function core.get_pointed_thing_position(pointed_thing, above) end end -function core.dir_to_facedir(dir, is6d) - --account for y if requested - if is6d and math.abs(dir.y) > math.abs(dir.x) and math.abs(dir.y) > math.abs(dir.z) then - - --from above - if dir.y < 0 then - if math.abs(dir.x) > math.abs(dir.z) then - if dir.x < 0 then - return 19 - else - return 13 - end - else - if dir.z < 0 then - return 10 - else - return 4 - end - end - - --from below - else - if math.abs(dir.x) > math.abs(dir.z) then - if dir.x < 0 then - return 15 - else - return 17 - end - else - if dir.z < 0 then - return 6 - else - return 8 - end - end - end - - --otherwise, place horizontally - elseif math.abs(dir.x) > math.abs(dir.z) then - if dir.x < 0 then - return 3 - else - return 1 - end - else - if dir.z < 0 then - return 2 - else - return 0 - end - end -end - --- Table of possible dirs -local facedir_to_dir = { - vector.new( 0, 0, 1), - vector.new( 1, 0, 0), - vector.new( 0, 0, -1), - vector.new(-1, 0, 0), - vector.new( 0, -1, 0), - vector.new( 0, 1, 0), -} --- Mapping from facedir value to index in facedir_to_dir. -local facedir_to_dir_map = { - [0]=1, 2, 3, 4, - 5, 2, 6, 4, - 6, 2, 5, 4, - 1, 5, 3, 6, - 1, 6, 3, 5, - 1, 4, 3, 2, -} -function core.facedir_to_dir(facedir) - return facedir_to_dir[facedir_to_dir_map[facedir % 32]] -end - -function core.dir_to_wallmounted(dir) - if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then - if dir.y < 0 then - return 1 - else - return 0 - end - elseif math.abs(dir.x) > math.abs(dir.z) then - if dir.x < 0 then - return 3 - else - return 2 - end - else - if dir.z < 0 then - return 5 - else - return 4 - end - end -end - --- table of dirs in wallmounted order -local wallmounted_to_dir = { - [0] = vector.new( 0, 1, 0), - vector.new( 0, -1, 0), - vector.new( 1, 0, 0), - vector.new(-1, 0, 0), - vector.new( 0, 0, 1), - vector.new( 0, 0, -1), -} -function core.wallmounted_to_dir(wallmounted) - return wallmounted_to_dir[wallmounted % 8] -end - -function core.dir_to_yaw(dir) - return -math.atan2(dir.x, dir.z) -end - -function core.yaw_to_dir(yaw) - return vector.new(-math.sin(yaw), 0, math.cos(yaw)) -end - -function core.is_colored_paramtype(ptype) - return (ptype == "color") or (ptype == "colorfacedir") or - (ptype == "colorwallmounted") or (ptype == "colordegrotate") -end - -function core.strip_param2_color(param2, paramtype2) - if not core.is_colored_paramtype(paramtype2) then - return nil - end - if paramtype2 == "colorfacedir" then - param2 = math.floor(param2 / 32) * 32 - elseif paramtype2 == "colorwallmounted" then - param2 = math.floor(param2 / 8) * 8 - elseif paramtype2 == "colordegrotate" then - param2 = math.floor(param2 / 32) * 32 - end - -- paramtype2 == "color" requires no modification. - return param2 -end - local function has_all_groups(tbl, required_groups) if type(required_groups) == "string" then return (tbl[required_groups] or 0) ~= 0 @@ -323,12 +176,12 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2, end -- Place above pointed node - local place_to = vector.new(above) + local place_to = vector.copy(above) -- If node under is buildable_to, place into it instead (eg. snow) if olddef_under.buildable_to then log("info", "node under is buildable to") - place_to = vector.new(under) + place_to = vector.copy(under) end if core.is_protected(place_to, playername) then @@ -409,7 +262,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2, -- Run callback if def.after_place_node and not prevent_after_place then -- Deepcopy place_to and pointed_thing because callback can modify it - local place_to_copy = vector.new(place_to) + local place_to_copy = vector.copy(place_to) local pointed_thing_copy = copy_pointed_thing(pointed_thing) if def.after_place_node(place_to_copy, placer, itemstack, pointed_thing_copy) then @@ -420,7 +273,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2, -- Run script hook for _, callback in ipairs(core.registered_on_placenodes) do -- Deepcopy pos, node and pointed_thing because callback can modify them - local place_to_copy = vector.new(place_to) + local place_to_copy = vector.copy(place_to) local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2} local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2} local pointed_thing_copy = copy_pointed_thing(pointed_thing) @@ -548,7 +401,7 @@ function core.node_punch(pos, node, puncher, pointed_thing) -- Run script hook for _, callback in ipairs(core.registered_on_punchnodes) do -- Copy pos and node because callback can modify them - local pos_copy = vector.new(pos) + local pos_copy = vector.copy(pos) local node_copy = {name=node.name, param1=node.param1, param2=node.param2} local pointed_thing_copy = pointed_thing and copy_pointed_thing(pointed_thing) or nil callback(pos_copy, node_copy, puncher, pointed_thing_copy) @@ -589,7 +442,7 @@ function core.node_dig(pos, node, digger) local def = core.registered_nodes[node.name] -- Copy pos because the callback could modify it if def and (not def.diggable or - (def.can_dig and not def.can_dig(vector.new(pos), digger))) then + (def.can_dig and not def.can_dig(vector.copy(pos), digger))) then log("info", diggername .. " tried to dig " .. node.name .. " which is not diggable " .. core.pos_to_string(pos)) @@ -636,7 +489,7 @@ function core.node_dig(pos, node, digger) if def and def.preserve_metadata then local oldmeta = core.get_meta(pos):to_table().fields -- Copy pos and node because the callback can modify them. - local pos_copy = vector.new(pos) + local pos_copy = vector.copy(pos) local node_copy = {name=node.name, param1=node.param1, param2=node.param2} local drop_stacks = {} for k, v in pairs(drops) do @@ -668,7 +521,7 @@ function core.node_dig(pos, node, digger) -- Run callback if def and def.after_dig_node then -- Copy pos and node because callback can modify them - local pos_copy = vector.new(pos) + local pos_copy = vector.copy(pos) local node_copy = {name=node.name, param1=node.param1, param2=node.param2} def.after_dig_node(pos_copy, node_copy, oldmetadata, digger) end @@ -679,7 +532,7 @@ function core.node_dig(pos, node, digger) core.set_last_run_mod(origin.mod) -- Copy pos and node because callback can modify them - local pos_copy = vector.new(pos) + local pos_copy = vector.copy(pos) local node_copy = {name=node.name, param1=node.param1, param2=node.param2} callback(pos_copy, node_copy, digger) end @@ -744,13 +597,8 @@ core.nodedef_default = { -- Node properties drawtype = "normal", visual_scale = 1.0, - -- Don't define these because otherwise the old tile_images and - -- special_materials wouldn't be read - --tiles ={""}, - --special_tiles = { - -- {name="", backface_culling=true}, - -- {name="", backface_culling=true}, - --}, + tiles = {}, + special_tiles = {}, post_effect_color = {a=0, r=0, g=0, b=0}, paramtype = "none", paramtype2 = "none", diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index 9b1b23bfd..53f98a7c7 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -58,17 +58,21 @@ core.register_entity(":__builtin:item", { local glow = def and def.light_source and math.floor(def.light_source / 2 + 0.5) + local size_bias = 1e-3 * math.random() -- small random bias to counter Z-fighting + local c = {-size, -size, -size, size, size, size} self.object:set_properties({ is_visible = true, visual = "wielditem", textures = {itemname}, - visual_size = {x = size, y = size}, - collisionbox = {-size, -size, -size, size, size, size}, + visual_size = {x = size + size_bias, y = size + size_bias}, + collisionbox = c, automatic_rotate = math.pi * 0.5 * 0.2 / size, wield_item = self.itemstring, glow = glow, }) + -- cache for usage in on_step + self._collisionbox = c end, get_staticdata = function(self) @@ -93,6 +97,7 @@ core.register_entity(":__builtin:item", { self.object:set_armor_groups({immortal = 1}) self.object:set_velocity({x = 0, y = 2, z = 0}) self.object:set_acceleration({x = 0, y = -gravity, z = 0}) + self._collisionbox = self.initial_properties.collisionbox self:set_item() end, @@ -163,7 +168,7 @@ core.register_entity(":__builtin:item", { local pos = self.object:get_pos() local node = core.get_node_or_nil({ x = pos.x, - y = pos.y + self.object:get_properties().collisionbox[2] - 0.05, + y = pos.y + self._collisionbox[2] - 0.05, z = pos.z }) -- Delete in 'ignore' nodes @@ -176,7 +181,7 @@ core.register_entity(":__builtin:item", { if self.force_out then -- This code runs after the entity got a push from the is_stuck code. -- It makes sure the entity is entirely outside the solid node - local c = self.object:get_properties().collisionbox + local c = self._collisionbox local s = self.force_out_start local f = self.force_out local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or diff --git a/builtin/game/item_s.lua b/builtin/game/item_s.lua new file mode 100644 index 000000000..a51cd0a1c --- /dev/null +++ b/builtin/game/item_s.lua @@ -0,0 +1,156 @@ +-- Minetest: builtin/item_s.lua +-- The distinction of what goes here is a bit tricky, basically it's everything +-- that does not (directly or indirectly) need access to ServerEnvironment, +-- Server or writable access to IGameDef on the engine side. +-- (The '_s' stands for standalone.) + +-- +-- Item definition helpers +-- + +function core.inventorycube(img1, img2, img3) + img2 = img2 or img1 + img3 = img3 or img1 + return "[inventorycube" + .. "{" .. img1:gsub("%^", "&") + .. "{" .. img2:gsub("%^", "&") + .. "{" .. img3:gsub("%^", "&") +end + +function core.dir_to_facedir(dir, is6d) + --account for y if requested + if is6d and math.abs(dir.y) > math.abs(dir.x) and math.abs(dir.y) > math.abs(dir.z) then + + --from above + if dir.y < 0 then + if math.abs(dir.x) > math.abs(dir.z) then + if dir.x < 0 then + return 19 + else + return 13 + end + else + if dir.z < 0 then + return 10 + else + return 4 + end + end + + --from below + else + if math.abs(dir.x) > math.abs(dir.z) then + if dir.x < 0 then + return 15 + else + return 17 + end + else + if dir.z < 0 then + return 6 + else + return 8 + end + end + end + + --otherwise, place horizontally + elseif math.abs(dir.x) > math.abs(dir.z) then + if dir.x < 0 then + return 3 + else + return 1 + end + else + if dir.z < 0 then + return 2 + else + return 0 + end + end +end + +-- Table of possible dirs +local facedir_to_dir = { + vector.new( 0, 0, 1), + vector.new( 1, 0, 0), + vector.new( 0, 0, -1), + vector.new(-1, 0, 0), + vector.new( 0, -1, 0), + vector.new( 0, 1, 0), +} +-- Mapping from facedir value to index in facedir_to_dir. +local facedir_to_dir_map = { + [0]=1, 2, 3, 4, + 5, 2, 6, 4, + 6, 2, 5, 4, + 1, 5, 3, 6, + 1, 6, 3, 5, + 1, 4, 3, 2, +} +function core.facedir_to_dir(facedir) + return facedir_to_dir[facedir_to_dir_map[facedir % 32]] +end + +function core.dir_to_wallmounted(dir) + if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then + if dir.y < 0 then + return 1 + else + return 0 + end + elseif math.abs(dir.x) > math.abs(dir.z) then + if dir.x < 0 then + return 3 + else + return 2 + end + else + if dir.z < 0 then + return 5 + else + return 4 + end + end +end + +-- table of dirs in wallmounted order +local wallmounted_to_dir = { + [0] = vector.new( 0, 1, 0), + vector.new( 0, -1, 0), + vector.new( 1, 0, 0), + vector.new(-1, 0, 0), + vector.new( 0, 0, 1), + vector.new( 0, 0, -1), +} +function core.wallmounted_to_dir(wallmounted) + return wallmounted_to_dir[wallmounted % 8] +end + +function core.dir_to_yaw(dir) + return -math.atan2(dir.x, dir.z) +end + +function core.yaw_to_dir(yaw) + return vector.new(-math.sin(yaw), 0, math.cos(yaw)) +end + +function core.is_colored_paramtype(ptype) + return (ptype == "color") or (ptype == "colorfacedir") or + (ptype == "colorwallmounted") or (ptype == "colordegrotate") +end + +function core.strip_param2_color(param2, paramtype2) + if not core.is_colored_paramtype(paramtype2) then + return nil + end + if paramtype2 == "colorfacedir" then + param2 = math.floor(param2 / 32) * 32 + elseif paramtype2 == "colorwallmounted" then + param2 = math.floor(param2 / 8) * 8 + elseif paramtype2 == "colordegrotate" then + param2 = math.floor(param2 / 32) * 32 + end + -- paramtype2 == "color" requires no modification. + return param2 +end diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index e86efc50c..997b1894a 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -121,53 +121,6 @@ function core.get_player_radius_area(player_name, radius) end -function core.hash_node_position(pos) - return (pos.z + 32768) * 65536 * 65536 - + (pos.y + 32768) * 65536 - + pos.x + 32768 -end - - -function core.get_position_from_hash(hash) - local x = (hash % 65536) - 32768 - hash = math.floor(hash / 65536) - local y = (hash % 65536) - 32768 - hash = math.floor(hash / 65536) - local z = (hash % 65536) - 32768 - return vector.new(x, y, z) -end - - -function core.get_item_group(name, group) - if not core.registered_items[name] or not - core.registered_items[name].groups[group] then - return 0 - end - return core.registered_items[name].groups[group] -end - - -function core.get_node_group(name, group) - core.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead") - return core.get_item_group(name, group) -end - - -function core.setting_get_pos(name) - local value = core.settings:get(name) - if not value then - return nil - end - return core.string_to_pos(value) -end - - --- See l_env.cpp for the other functions -function core.get_artificial_light(param1) - return math.floor(param1 / 16) -end - - -- To be overriden by protection mods function core.is_protected(pos, name) @@ -284,40 +237,30 @@ end core.dynamic_media_callbacks = {} --- PNG encoder safety wrapper +-- Transfer of certain globals into async environment +-- see builtin/async/game.lua for the other side -local o_encode_png = core.encode_png -function core.encode_png(width, height, data, compression) - if type(width) ~= "number" then - error("Incorrect type for 'width', expected number, got " .. type(width)) - end - if type(height) ~= "number" then - error("Incorrect type for 'height', expected number, got " .. type(height)) +local function copy_filtering(t, seen) + if type(t) == "userdata" or type(t) == "function" then + return true -- don't use nil so presence can still be detected + elseif type(t) ~= "table" then + return t end - - local expected_byte_count = width * height * 4 - - if type(data) ~= "table" and type(data) ~= "string" then - error("Incorrect type for 'data', expected table or string, got " .. type(data)) - end - - local data_length = type(data) == "table" and #data * 4 or string.len(data) - - if data_length ~= expected_byte_count then - error(string.format( - "Incorrect length of 'data', width and height imply %d bytes but %d were provided", - expected_byte_count, - data_length - )) - end - - if type(data) == "table" then - local dataBuf = {} - for i = 1, #data do - dataBuf[i] = core.colorspec_to_bytes(data[i]) - end - data = table.concat(dataBuf) + local n = {} + seen = seen or {} + seen[t] = n + for k, v in pairs(t) do + local k_ = seen[k] or copy_filtering(k, seen) + local v_ = seen[v] or copy_filtering(v, seen) + n[k_] = v_ end + return n +end - return o_encode_png(width, height, data, compression or 6) +function core.get_globals_to_transfer() + local all = { + registered_items = copy_filtering(core.registered_items), + registered_aliases = core.registered_aliases, + } + return all end diff --git a/builtin/game/misc_s.lua b/builtin/game/misc_s.lua new file mode 100644 index 000000000..67a0ec684 --- /dev/null +++ b/builtin/game/misc_s.lua @@ -0,0 +1,93 @@ +-- Minetest: builtin/misc_s.lua +-- The distinction of what goes here is a bit tricky, basically it's everything +-- that does not (directly or indirectly) need access to ServerEnvironment, +-- Server or writable access to IGameDef on the engine side. +-- (The '_s' stands for standalone.) + +-- +-- Misc. API functions +-- + +function core.hash_node_position(pos) + return (pos.z + 32768) * 65536 * 65536 + + (pos.y + 32768) * 65536 + + pos.x + 32768 +end + + +function core.get_position_from_hash(hash) + local x = (hash % 65536) - 32768 + hash = math.floor(hash / 65536) + local y = (hash % 65536) - 32768 + hash = math.floor(hash / 65536) + local z = (hash % 65536) - 32768 + return vector.new(x, y, z) +end + + +function core.get_item_group(name, group) + if not core.registered_items[name] or not + core.registered_items[name].groups[group] then + return 0 + end + return core.registered_items[name].groups[group] +end + + +function core.get_node_group(name, group) + core.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead") + return core.get_item_group(name, group) +end + + +function core.setting_get_pos(name) + local value = core.settings:get(name) + if not value then + return nil + end + return core.string_to_pos(value) +end + + +-- See l_env.cpp for the other functions +function core.get_artificial_light(param1) + return math.floor(param1 / 16) +end + +-- PNG encoder safety wrapper + +local o_encode_png = core.encode_png +function core.encode_png(width, height, data, compression) + if type(width) ~= "number" then + error("Incorrect type for 'width', expected number, got " .. type(width)) + end + if type(height) ~= "number" then + error("Incorrect type for 'height', expected number, got " .. type(height)) + end + + local expected_byte_count = width * height * 4 + + if type(data) ~= "table" and type(data) ~= "string" then + error("Incorrect type for 'data', expected table or string, got " .. type(data)) + end + + local data_length = type(data) == "table" and #data * 4 or string.len(data) + + if data_length ~= expected_byte_count then + error(string.format( + "Incorrect length of 'data', width and height imply %d bytes but %d were provided", + expected_byte_count, + data_length + )) + end + + if type(data) == "table" then + local dataBuf = {} + for i = 1, #data do + dataBuf[i] = core.colorspec_to_bytes(data[i]) + end + data = table.concat(dataBuf) + end + + return o_encode_png(width, height, data, compression or 6) +end diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 0be107c36..ee4edabbf 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -1,4 +1,4 @@ --- Minetest: builtin/misc_register.lua +-- Minetest: builtin/register.lua local S = core.get_translator("__builtin") diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua index cb7ff7b76..78d1d2728 100644 --- a/builtin/game/statbars.lua +++ b/builtin/game/statbars.lua @@ -138,13 +138,15 @@ local function player_event_handler(player,eventname) end function core.hud_replace_builtin(hud_name, definition) - if type(definition) ~= "table" or definition.hud_elem_type ~= "statbar" then return false end + definition = table.copy(definition) + if hud_name == "health" then + definition.item = definition.item or definition.number or core.PLAYER_MAX_HP_DEFAULT bar_definitions.hp = definition for name, ids in pairs(hud_ids) do @@ -159,6 +161,7 @@ function core.hud_replace_builtin(hud_name, definition) end if hud_name == "breath" then + definition.item = definition.item or definition.number or core.PLAYER_MAX_BREATH_DEFAULT bar_definitions.breath = definition for name, ids in pairs(hud_ids) do diff --git a/builtin/game/voxelarea.lua b/builtin/game/voxelarea.lua index 64436bf1a..62f07d928 100644 --- a/builtin/game/voxelarea.lua +++ b/builtin/game/voxelarea.lua @@ -1,6 +1,9 @@ +local math_floor = math.floor +local vector_new = vector.new + VoxelArea = { - MinEdge = vector.new(1, 1, 1), - MaxEdge = vector.new(0, 0, 0), + MinEdge = vector_new(1, 1, 1), + MaxEdge = vector_new(0, 0, 0), ystride = 0, zstride = 0, } @@ -19,7 +22,7 @@ end function VoxelArea:getExtent() local MaxEdge, MinEdge = self.MaxEdge, self.MinEdge - return vector.new( + return vector_new( MaxEdge.x - MinEdge.x + 1, MaxEdge.y - MinEdge.y + 1, MaxEdge.z - MinEdge.z + 1 @@ -36,7 +39,7 @@ function VoxelArea:index(x, y, z) local i = (z - MinEdge.z) * self.zstride + (y - MinEdge.y) * self.ystride + (x - MinEdge.x) + 1 - return math.floor(i) + return math_floor(i) end function VoxelArea:indexp(p) @@ -44,24 +47,23 @@ function VoxelArea:indexp(p) local i = (p.z - MinEdge.z) * self.zstride + (p.y - MinEdge.y) * self.ystride + (p.x - MinEdge.x) + 1 - return math.floor(i) + return math_floor(i) end function VoxelArea:position(i) - local p = {} local MinEdge = self.MinEdge i = i - 1 - p.z = math.floor(i / self.zstride) + MinEdge.z + local z = math_floor(i / self.zstride) + MinEdge.z i = i % self.zstride - p.y = math.floor(i / self.ystride) + MinEdge.y + local y = math_floor(i / self.ystride) + MinEdge.y i = i % self.ystride - p.x = math.floor(i) + MinEdge.x + local x = math_floor(i) + MinEdge.x - return p + return vector_new(x, y, z) end function VoxelArea:contains(x, y, z) diff --git a/builtin/init.lua b/builtin/init.lua index 7a9b5c427..869136016 100644 --- a/builtin/init.lua +++ b/builtin/init.lua @@ -56,8 +56,10 @@ elseif INIT == "mainmenu" then if not custom_loaded then dofile(core.get_mainmenu_path() .. DIR_DELIM .. "init.lua") end -elseif INIT == "async" then - dofile(asyncpath .. "init.lua") +elseif INIT == "async" then + dofile(asyncpath .. "mainmenu.lua") +elseif INIT == "async_game" then + dofile(asyncpath .. "game.lua") elseif INIT == "client" then dofile(clientpath .. "init.lua") else diff --git a/builtin/locale/__builtin.de.tr b/builtin/locale/__builtin.de.tr index 1b29f81e7..4a17f7a4b 100644 --- a/builtin/locale/__builtin.de.tr +++ b/builtin/locale/__builtin.de.tr @@ -72,6 +72,7 @@ Teleport to position or player=Zu Position oder Spieler teleportieren You don't have permission to teleport other players (missing privilege: @1).=Sie haben nicht die Erlaubnis, andere Spieler zu teleportieren (fehlendes Privileg: @1). ([-n] <name> <value>) | <name>=([-n] <Name> <Wert>) | <Name> Set or read server configuration setting=Serverkonfigurationseinstellung setzen oder lesen +Failed. Cannot modify secure settings. Edit the settings file manually.=Fehlgeschlagen. Sicherheitseinstellungen können nicht modifiziert werden. Bearbeiten Sie die Einstellungsdatei manuell. Failed. Use '/set -n <name> <value>' to create a new setting.=Fehlgeschlagen. Benutzen Sie „/set -n <Name> <Wert>“, um eine neue Einstellung zu erstellen. @1 @= @2=@1 @= @2 <not set>=<nicht gesetzt> @@ -151,6 +152,7 @@ Server shutting down (operator request).=Server wird heruntergefahren (Betreiber Ban the IP of a player or show the ban list=Die IP eines Spielers verbannen oder die Bannliste anzeigen The ban list is empty.=Die Bannliste ist leer. Ban list: @1=Bannliste: @1 +You cannot ban players in singleplayer!=Im Einzelspielermodus können Sie keine Spieler verbannen! Player is not online.=Spieler ist nicht online. Failed to ban player.=Konnte Spieler nicht verbannen. Banned @1.=@1 verbannt. @@ -195,7 +197,6 @@ Available commands:=Verfügbare Befehle: Command not available: @1=Befehl nicht verfügbar: @1 [all | privs | <cmd>] [-t]=[all | privs | <Befehl>] [-t] Get help for commands or list privileges (-t: output in chat)=Hilfe für Befehle erhalten oder Privilegien auflisten (-t: Ausgabe im Chat) -Available privileges:=Verfügbare Privilegien: Command=Befehl Parameters=Parameter For more information, click on any entry in the list.=Für mehr Informationen klicken Sie auf einen beliebigen Eintrag in der Liste. @@ -205,6 +206,7 @@ Available commands: (see also: /help <cmd>)=Verfügbare Befehle: (siehe auch: /h Close=Schließen Privilege=Privileg Description=Beschreibung +Available privileges:=Verfügbare Privilegien: print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<Filter>] | dump [<Filter>] | save [<Format> [<Filter>]] Handle the profiler and profiling data=Den Profiler und Profilingdaten verwalten Statistics written to action log.=Statistiken zum Aktionsprotokoll geschrieben. @@ -232,7 +234,6 @@ Can use fly mode=Kann den Flugmodus benutzen Can use fast mode=Kann den Schnellmodus benutzen Can fly through solid nodes using noclip mode=Kann durch feste Blöcke mit dem Geistmodus fliegen Can use the rollback functionality=Kann die Rollback-Funktionalität benutzen -Can view more debug info that might give a gameplay advantage=Kann zusätzliche Debuginformationen betrachten, welche einen spielerischen Vorteil geben könnten Can enable wireframe=Kann Drahtmodell aktivieren Unknown Item=Unbekannter Gegenstand Air=Luft diff --git a/builtin/locale/__builtin.it.tr b/builtin/locale/__builtin.it.tr index 77f85c766..b04b48926 100644 --- a/builtin/locale/__builtin.it.tr +++ b/builtin/locale/__builtin.it.tr @@ -72,6 +72,7 @@ Teleport to position or player=Teletrasporta a una posizione o da un giocatore You don't have permission to teleport other players (missing privilege: @1).=Non hai il permesso di teletrasportare altri giocatori (privilegio mancante: @1). ([-n] <name> <value>) | <name>=([-n] <nome> <valore>) | <nome> Set or read server configuration setting=Imposta o ottieni le configurazioni del server +Failed. Cannot modify secure settings. Edit the settings file manually.= Failed. Use '/set -n <name> <value>' to create a new setting.=Errore. Usa 'set -n <nome> <valore>' per creare una nuova impostazione @1 @= @2=@1 @= @2 <not set>=<non impostato> @@ -151,6 +152,7 @@ Server shutting down (operator request).=Arresto del server in corso (per richie Ban the IP of a player or show the ban list=Bandisce l'IP del giocatore o mostra la lista di quelli banditi The ban list is empty.=La lista banditi è vuota. Ban list: @1=Lista banditi: @1 +You cannot ban players in singleplayer!= Player is not online.=Il giocatore non è connesso. Failed to ban player.=Errore nel bandire il giocatore. Banned @1.=@1 banditǝ. @@ -195,7 +197,6 @@ Available commands:=Comandi disponibili: Command not available: @1=Comando non disponibile: @1 [all | privs | <cmd>] [-t]= Get help for commands or list privileges (-t: output in chat)= -Available privileges:=Privilegi disponibili: Command=Comando Parameters=Parametri For more information, click on any entry in the list.=Per più informazioni, clicca su una qualsiasi voce dell'elenco. @@ -205,6 +206,7 @@ Available commands: (see also: /help <cmd>)=Comandi disponibili: (vedi anche /he Close=Chiudi Privilege=Privilegio Description=Descrizione +Available privileges:=Privilegi disponibili: print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filtro>] | dump [<filtro>] | save [<formato> [<filtro>]] | reset Handle the profiler and profiling data=Gestisce il profiler e i dati da esso elaborati Statistics written to action log.=Statistiche scritte nel log delle azioni. @@ -232,7 +234,6 @@ Can use fly mode=Si può usare la modalità volo Can use fast mode=Si può usare la modalità rapida Can fly through solid nodes using noclip mode=Si può volare attraverso i nodi solidi con la modalità incorporea Can use the rollback functionality=Si può usare la funzione di rollback -Can view more debug info that might give a gameplay advantage= Can enable wireframe= Unknown Item=Oggetto sconosciuto Air=Aria diff --git a/builtin/locale/template.txt b/builtin/locale/template.txt index 308d17f37..fa7352317 100644 --- a/builtin/locale/template.txt +++ b/builtin/locale/template.txt @@ -72,6 +72,7 @@ Teleport to position or player= You don't have permission to teleport other players (missing privilege: @1).= ([-n] <name> <value>) | <name>= Set or read server configuration setting= +Failed. Cannot modify secure settings. Edit the settings file manually.= Failed. Use '/set -n <name> <value>' to create a new setting.= @1 @= @2= <not set>= @@ -151,6 +152,7 @@ Server shutting down (operator request).= Ban the IP of a player or show the ban list= The ban list is empty.= Ban list: @1= +You cannot ban players in singleplayer!= Player is not online.= Failed to ban player.= Banned @1.= @@ -195,7 +197,6 @@ Available commands:= Command not available: @1= [all | privs | <cmd>] [-t]= Get help for commands or list privileges (-t: output in chat)= -Available privileges:= Command= Parameters= For more information, click on any entry in the list.= @@ -205,6 +206,7 @@ Available commands: (see also: /help <cmd>)= Close= Privilege= Description= +Available privileges:= print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset= Handle the profiler and profiling data= Statistics written to action log.= @@ -232,7 +234,6 @@ Can use fly mode= Can use fast mode= Can fly through solid nodes using noclip mode= Can use the rollback functionality= -Can view more debug info that might give a gameplay advantage= Can enable wireframe= Unknown Item= Air= diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua index 8db8bb8d1..81e28f2bb 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/common.lua @@ -125,18 +125,12 @@ os.tmpname = function() end -------------------------------------------------------------------------------- -function menu_render_worldlist(show_gameid) +function menu_render_worldlist() local retval = {} local current_worldlist = menudata.worldlist:get_list() - local row for i, v in ipairs(current_worldlist) do - row = v.name - if show_gameid == nil or show_gameid == true then - row = row .. " [" .. v.gameid .. "]" - end - retval[#retval+1] = core.formspec_escape(row) - + retval[#retval+1] = core.formspec_escape(v.name) end return table.concat(retval, ",") @@ -242,3 +236,11 @@ function menu_worldmt_legacy(selected) end end end + +function confirmation_formspec(message, confirm_id, confirm_label, cancel_id, cancel_label) + return "size[10,2.5,true]" .. + "label[0.5,0.5;" .. message .. "]" .. + "style[" .. confirm_id .. ";bgcolor=red]" .. + "button[0.5,1.5;2.5,0.5;" .. confirm_id .. ";" .. confirm_label .. "]" .. + "button[7.0,1.5;2.5,0.5;" .. cancel_id .. ";" .. cancel_label .. "]" +end diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/dlg_config_world.lua index 9bdf92a74..e76e10ef7 100644 --- a/builtin/mainmenu/dlg_config_world.lua +++ b/builtin/mainmenu/dlg_config_world.lua @@ -61,12 +61,68 @@ local function init_data(data) data.list:set_sortmode("alphabetic") end + +-- Returns errors errors and a list of all enabled mods (inc. game and world mods) +-- +-- `with_errors` is a table from mod virtual path to `{ type = "error" | "warning" }`. +-- `enabled_mods_by_name` is a table from mod virtual path to `true`. +-- +-- @param world_path Path to the world +-- @param all_mods List of mods, with `enabled` property. +-- @returns with_errors, enabled_mods_by_name +local function check_mod_configuration(world_path, all_mods) + -- Build up lookup tables for enabled mods and all mods by vpath + local enabled_mod_paths = {} + local all_mods_by_vpath = {} + for _, mod in ipairs(all_mods) do + if mod.type == "mod" then + all_mods_by_vpath[mod.virtual_path] = mod + end + if mod.enabled then + enabled_mod_paths[mod.virtual_path] = mod.path + end + end + + -- Use the engine's mod configuration code to resolve dependencies and return any errors + local config_status = core.check_mod_configuration(world_path, enabled_mod_paths) + + -- Build the list of enabled mod virtual paths + local enabled_mods_by_name = {} + for _, mod in ipairs(config_status.satisfied_mods) do + assert(mod.virtual_path ~= "") + enabled_mods_by_name[mod.name] = all_mods_by_vpath[mod.virtual_path] or mod + end + for _, mod in ipairs(config_status.unsatisfied_mods) do + assert(mod.virtual_path ~= "") + enabled_mods_by_name[mod.name] = all_mods_by_vpath[mod.virtual_path] or mod + end + + -- Build the table of errors + local with_error = {} + for _, mod in ipairs(config_status.unsatisfied_mods) do + local error = { type = "warning" } + with_error[mod.virtual_path] = error + + for _, depname in ipairs(mod.unsatisfied_depends) do + if not enabled_mods_by_name[depname] then + error.type = "error" + break + end + end + end + + return with_error, enabled_mods_by_name +end + local function get_formspec(data) if not data.list then init_data(data) end - local mod = data.list:get_list()[data.selected_mod] or {name = ""} + local all_mods = data.list:get_list() + local with_error, enabled_mods_by_name = check_mod_configuration(data.worldspec.path, all_mods) + + local mod = all_mods[data.selected_mod] or {name = ""} local retval = "size[11.5,7.5,true]" .. @@ -87,6 +143,29 @@ local function get_formspec(data) "textarea[0.25,0.7;5.75,7.2;;" .. info .. ";]" else local hard_deps, soft_deps = pkgmgr.get_dependencies(mod.path) + + -- Add error messages to dep lists + if mod.enabled or mod.is_game_content then + for i, dep_name in ipairs(hard_deps) do + local dep = enabled_mods_by_name[dep_name] + if not dep then + hard_deps[i] = mt_color_red .. dep_name .. " " .. fgettext("(Unsatisfied)") + elseif with_error[dep.virtual_path] then + hard_deps[i] = mt_color_orange .. dep_name .. " " .. fgettext("(Enabled, has error)") + else + hard_deps[i] = mt_color_green .. dep_name + end + end + for i, dep_name in ipairs(soft_deps) do + local dep = enabled_mods_by_name[dep_name] + if dep and with_error[dep.virtual_path] then + soft_deps[i] = mt_color_orange .. dep_name .. " " .. fgettext("(Enabled, has error)") + elseif dep then + soft_deps[i] = mt_color_green .. dep_name + end + end + end + local hard_deps_str = table.concat(hard_deps, ",") local soft_deps_str = table.concat(soft_deps, ",") @@ -138,7 +217,6 @@ local function get_formspec(data) if mod.name ~= "" and not mod.is_game_content then if mod.is_modpack then - if pkgmgr.is_modpack_entirely_enabled(data, mod.name) then retval = retval .. "button[5.5,0.125;3,0.5;btn_mp_disable;" .. @@ -163,10 +241,16 @@ local function get_formspec(data) "button[8.95,0.125;2.5,0.5;btn_enable_all_mods;" .. fgettext("Enable all") .. "]" end + + local use_technical_names = core.settings:get_bool("show_technical_names") + return retval .. - "tablecolumns[color;tree;text]" .. + "tablecolumns[color;tree;image,align=inline,width=1.5,0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") .. + ",1=" .. core.formspec_escape(defaulttexturedir .. "checkbox_16_white.png") .. + ",2=" .. core.formspec_escape(defaulttexturedir .. "error_icon_orange.png") .. + ",3=" .. core.formspec_escape(defaulttexturedir .. "error_icon_red.png") .. ";text]" .. "table[5.5,0.75;5.75,6;world_config_modlist;" .. - pkgmgr.render_packagelist(data.list) .. ";" .. data.selected_mod .."]" + pkgmgr.render_packagelist(data.list, use_technical_names, with_error) .. ";" .. data.selected_mod .."]" end local function handle_buttons(this, fields) @@ -205,14 +289,19 @@ local function handle_buttons(this, fields) local mods = worldfile:to_table() local rawlist = this.data.list:get_raw_list() + local was_set = {} for i = 1, #rawlist do local mod = rawlist[i] if not mod.is_modpack and not mod.is_game_content then if modname_valid(mod.name) then - worldfile:set("load_mod_" .. mod.name, - mod.enabled and "true" or "false") + if mod.enabled then + worldfile:set("load_mod_" .. mod.name, mod.virtual_path) + was_set[mod.name] = true + elseif not was_set[mod.name] then + worldfile:set("load_mod_" .. mod.name, "false") + end elseif mod.enabled then gamedata.errormessage = fgettext_ne("Failed to enable mo" .. "d \"$1\" as it contains disallowed characters. " .. @@ -256,12 +345,26 @@ local function handle_buttons(this, fields) if fields.btn_enable_all_mods then local list = this.data.list:get_raw_list() + -- When multiple copies of a mod are installed, we need to avoid enabling multiple of them + -- at a time. So lets first collect all the enabled mods, and then use this to exclude + -- multiple enables. + + local was_enabled = {} for i = 1, #list do if not list[i].is_game_content - and not list[i].is_modpack then + and not list[i].is_modpack and list[i].enabled then + was_enabled[list[i].name] = true + end + end + + for i = 1, #list do + if not list[i].is_game_content and not list[i].is_modpack and + not was_enabled[list[i].name] then list[i].enabled = true + was_enabled[list[i].name] = true end end + enabled_all = true return true end diff --git a/builtin/mainmenu/dlg_contentstore.lua b/builtin/mainmenu/dlg_contentstore.lua index 276a7b096..2152b8a39 100644 --- a/builtin/mainmenu/dlg_contentstore.lua +++ b/builtin/mainmenu/dlg_contentstore.lua @@ -151,11 +151,9 @@ local function start_install(package, reason) if conf_path then local conf = Settings(conf_path) - if name_is_title then - conf:set("name", package.title) - else - conf:set("title", package.title) - conf:set("name", package.name) + conf:set("title", package.title) + if not name_is_title then + conf:set("name", package.name) end if not conf:get("description") then conf:set("description", package.short_description) @@ -193,7 +191,7 @@ end local function queue_download(package, reason) local max_concurrent_downloads = tonumber(core.settings:get("contentdb_max_concurrent_downloads")) - if number_downloading < max_concurrent_downloads then + if number_downloading < math.max(max_concurrent_downloads, 1) then start_install(package, reason) else table.insert(download_queue, { package = package, reason = reason }) @@ -360,7 +358,7 @@ function install_dialog.get_formspec() selected_game_idx = i end - games[i] = core.formspec_escape(games[i].name) + games[i] = core.formspec_escape(games[i].title) end local selected_game = pkgmgr.games[selected_game_idx] @@ -410,7 +408,7 @@ function install_dialog.get_formspec() "container[0.375,0.70]", "label[0,0.25;", fgettext("Base Game:"), "]", - "dropdown[2,0;4.25,0.5;gameid;", table.concat(games, ","), ";", selected_game_idx, "]", + "dropdown[2,0;4.25,0.5;selected_game;", table.concat(games, ","), ";", selected_game_idx, "]", "label[0,0.8;", fgettext("Dependencies:"), "]", @@ -461,9 +459,9 @@ function install_dialog.handle_submit(this, fields) return true end - if fields.gameid then + if fields.selected_game then for _, game in pairs(pkgmgr.games) do - if game.name == fields.gameid then + if game.title == fields.selected_game then core.settings:set("menu_last_game", game.id) break end @@ -490,12 +488,10 @@ local confirm_overwrite = {} function confirm_overwrite.get_formspec() local package = confirm_overwrite.package - return "size[11.5,4.5,true]" .. - "label[2,2;" .. - fgettext("\"$1\" already exists. Would you like to overwrite it?", package.name) .. "]".. - "style[install;bgcolor=red]" .. - "button[3.25,3.5;2.5,0.5;install;" .. fgettext("Overwrite") .. "]" .. - "button[5.75,3.5;2.5,0.5;cancel;" .. fgettext("Cancel") .. "]" + return confirmation_formspec( + fgettext("\"$1\" already exists. Would you like to overwrite it?", package.name), + 'install', fgettext("Overwrite"), + 'cancel', fgettext("Cancel")) end function confirm_overwrite.handle_submit(this, fields) diff --git a/builtin/mainmenu/dlg_create_world.lua b/builtin/mainmenu/dlg_create_world.lua index 8d1509f33..806e019a9 100644 --- a/builtin/mainmenu/dlg_create_world.lua +++ b/builtin/mainmenu/dlg_create_world.lua @@ -15,9 +15,6 @@ --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --- cf. tab_local, the gamebar already provides game selection so we hide the list from here -local hide_gamelist = PLATFORM ~= "Android" - local function table_to_flags(ftable) -- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves" local str = {} @@ -94,14 +91,14 @@ local mgv6_biomes = { local function create_world_formspec(dialogdata) - -- Error out when no games found + -- Point the player to ContentDB when no games are found if #pkgmgr.games == 0 then - return "size[12.25,3,true]" .. - "box[0,0;12,2;" .. mt_color_orange .. "]" .. - "textarea[0.3,0;11.7,2;;;".. - fgettext("You have no games installed.") .. "\n" .. - fgettext("Download one from minetest.net") .. "]" .. - "button[4.75,2.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" + return "size[8,2.5,true]" .. + "style[label_button;border=false]" .. + "button[0.5,0.5;7,0.5;label_button;" .. + fgettext("You have no games installed.") .. "]" .. + "button[0.5,1.5;2.5,0.5;world_create_open_cdb;" .. fgettext("Install a game") .. "]" .. + "button[5.0,1.5;2.5,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" end local current_mg = dialogdata.mg @@ -111,14 +108,11 @@ local function create_world_formspec(dialogdata) local flags = dialogdata.flags - local game, gameidx = pkgmgr.find_by_gameid(gameid) - if game == nil and hide_gamelist then + local game = pkgmgr.find_by_gameid(gameid) + if game == nil then -- should never happen but just pick the first game game = pkgmgr.get_game(1) - gameidx = 1 core.settings:set("menu_last_game", game.id) - elseif game == nil then - gameidx = 0 end local disallowed_mapgen_settings = {} @@ -296,17 +290,6 @@ local function create_world_formspec(dialogdata) label_spflags = "label[0,"..y_start..";" .. fgettext("Mapgen-specific flags") .. "]" end - -- Warning if only devtest is installed - local devtest_only = "" - local gamelist_height = 2.3 - if #pkgmgr.games == 1 and pkgmgr.games[1].id == "devtest" then - devtest_only = "box[0,0;5.8,1.7;#ff8800]" .. - "textarea[0.3,0;6,1.8;;;".. - fgettext("Warning: The Development Test is meant for developers.") .. "\n" .. - fgettext("Download a game, such as Minetest Game, from minetest.net") .. "]" - gamelist_height = 0.5 - end - local retval = "size[12.25,7,true]" .. @@ -315,22 +298,28 @@ local function create_world_formspec(dialogdata) "field[0.3,0.6;6,0.5;te_world_name;" .. fgettext("World name") .. ";" .. core.formspec_escape(dialogdata.worldname) .. "]" .. - "set_focus[te_world_name;false]" .. + "set_focus[te_world_name;false]" - "field[0.3,1.7;6,0.5;te_seed;" .. - fgettext("Seed") .. - ";".. core.formspec_escape(dialogdata.seed) .. "]" .. + if not disallowed_mapgen_settings["seed"] then + retval = retval .. "field[0.3,1.7;6,0.5;te_seed;" .. + fgettext("Seed") .. + ";".. core.formspec_escape(dialogdata.seed) .. "]" + + end + + retval = retval .. "label[0,2;" .. fgettext("Mapgen") .. "]".. "dropdown[0,2.5;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" - if not hide_gamelist or devtest_only ~= "" then + -- Warning if only devtest is installed + if #pkgmgr.games == 1 and pkgmgr.games[1].id == "devtest" then retval = retval .. - "label[0,3.35;" .. fgettext("Game") .. "]".. - "textlist[0,3.85;5.8,"..gamelist_height..";games;" .. - pkgmgr.gamelist() .. ";" .. gameidx .. ";false]" .. - "container[0,4.5]" .. - devtest_only .. + "container[0,3.5]" .. + "box[0,0;5.8,1.7;#ff8800]" .. + "textarea[0.4,0.1;6,1.8;;;".. + fgettext("Development Test is meant for developers.") .. "]" .. + "button[1,1;4,0.5;world_create_open_cdb;" .. fgettext("Install another game") .. "]" .. "container_end[]" end @@ -353,17 +342,20 @@ end local function create_world_buttonhandler(this, fields) + if fields["world_create_open_cdb"] then + local dlg = create_store_dlg("game") + dlg:set_parent(this.parent) + this:delete() + this.parent:hide() + dlg:show() + return true + end + if fields["world_create_confirm"] or fields["key_enter"] then local worldname = fields["te_world_name"] - local game, gameindex - if hide_gamelist then - game, gameindex = pkgmgr.find_by_gameid(core.settings:get("menu_last_game")) - else - gameindex = core.get_textlist_index("games") - game = pkgmgr.get_game(gameindex) - end + local game, gameindex = pkgmgr.find_by_gameid(core.settings:get("menu_last_game")) local message if game == nil then @@ -391,7 +383,7 @@ local function create_world_buttonhandler(this, fields) end if message == nil then - this.data.seed = fields["te_seed"] + this.data.seed = fields["te_seed"] or "" this.data.mg = fields["dd_mapgen"] -- actual names as used by engine @@ -412,9 +404,7 @@ local function create_world_buttonhandler(this, fields) if message == nil then core.settings:set("menu_last_game", game.id) - if this.data.update_worldlist_filter then - menudata.worldlist:set_filtercriteria(game.id) - end + menudata.worldlist:set_filtercriteria(game.id) menudata.worldlist:refresh() core.settings:set("mainmenu_last_selected_world", menudata.worldlist:raw_index_by_uid(worldname)) @@ -426,7 +416,7 @@ local function create_world_buttonhandler(this, fields) end this.data.worldname = fields["te_world_name"] - this.data.seed = fields["te_seed"] + this.data.seed = fields["te_seed"] or "" if fields["games"] then local gameindex = core.get_textlist_index("games") @@ -472,13 +462,12 @@ local function create_world_buttonhandler(this, fields) end -function create_create_world_dlg(update_worldlistfilter) +function create_create_world_dlg() local retval = dialog_create("sp_create_world", create_world_formspec, create_world_buttonhandler, nil) retval.data = { - update_worldlist_filter = update_worldlistfilter, worldname = "", -- settings the world is created with: seed = core.settings:get("fixed_map_seed") or "", diff --git a/builtin/mainmenu/dlg_delete_content.lua b/builtin/mainmenu/dlg_delete_content.lua index a24171541..4463825f7 100644 --- a/builtin/mainmenu/dlg_delete_content.lua +++ b/builtin/mainmenu/dlg_delete_content.lua @@ -18,15 +18,10 @@ -------------------------------------------------------------------------------- local function delete_content_formspec(dialogdata) - local retval = - "size[11.5,4.5,true]" .. - "label[2,2;" .. - fgettext("Are you sure you want to delete \"$1\"?", dialogdata.content.name) .. "]".. - "style[dlg_delete_content_confirm;bgcolor=red]" .. - "button[3.25,3.5;2.5,0.5;dlg_delete_content_confirm;" .. fgettext("Delete") .. "]" .. - "button[5.75,3.5;2.5,0.5;dlg_delete_content_cancel;" .. fgettext("Cancel") .. "]" - - return retval + return confirmation_formspec( + fgettext("Are you sure you want to delete \"$1\"?", dialogdata.content.name), + 'dlg_delete_content_confirm', fgettext("Delete"), + 'dlg_delete_content_cancel', fgettext("Cancel")) end -------------------------------------------------------------------------------- diff --git a/builtin/mainmenu/dlg_delete_world.lua b/builtin/mainmenu/dlg_delete_world.lua index 33e7bc945..67c0612bd 100644 --- a/builtin/mainmenu/dlg_delete_world.lua +++ b/builtin/mainmenu/dlg_delete_world.lua @@ -17,14 +17,10 @@ local function delete_world_formspec(dialogdata) - local retval = - "size[10,2.5,true]" .. - "label[0.5,0.5;" .. - fgettext("Delete World \"$1\"?", dialogdata.delete_name) .. "]" .. - "style[world_delete_confirm;bgcolor=red]" .. - "button[0.5,1.5;2.5,0.5;world_delete_confirm;" .. fgettext("Delete") .. "]" .. - "button[7.0,1.5;2.5,0.5;world_delete_cancel;" .. fgettext("Cancel") .. "]" - return retval + return confirmation_formspec( + fgettext("Delete World \"$1\"?", dialogdata.delete_name), + 'world_delete_confirm', fgettext("Delete"), + 'world_delete_cancel', fgettext("Cancel")) end local function delete_world_buttonhandler(this, fields) diff --git a/builtin/mainmenu/dlg_register.lua b/builtin/mainmenu/dlg_register.lua new file mode 100644 index 000000000..a7658249c --- /dev/null +++ b/builtin/mainmenu/dlg_register.lua @@ -0,0 +1,123 @@ +--Minetest +--Copyright (C) 2022 rubenwardy +-- +--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 function register_formspec(dialogdata) + local title = fgettext("Joining $1", dialogdata.server and dialogdata.server.name or dialogdata.address) + local buttons_y = 4 + 1.3 + if dialogdata.error then + buttons_y = buttons_y + 0.8 + end + + local retval = { + "formspec_version[4]", + "size[8,", tostring(buttons_y + 1.175), "]", + "set_focus[", (dialogdata.name ~= "" and "password" or "name"), "]", + "label[0.375,0.8;", title, "]", + "field[0.375,1.575;7.25,0.8;name;", core.formspec_escape(fgettext("Name")), ";", + core.formspec_escape(dialogdata.name), "]", + "pwdfield[0.375,2.875;7.25,0.8;password;", core.formspec_escape(fgettext("Password")), "]", + "pwdfield[0.375,4.175;7.25,0.8;password_2;", core.formspec_escape(fgettext("Confirm Password")), "]" + } + + if dialogdata.error then + table.insert_all(retval, { + "box[0.375,", tostring(buttons_y - 0.9), ";7.25,0.6;darkred]", + "label[0.625,", tostring(buttons_y - 0.6), ";", core.formspec_escape(dialogdata.error), "]", + }) + end + + table.insert_all(retval, { + "container[0.375,", tostring(buttons_y), "]", + "button[0,0;2.5,0.8;dlg_register_confirm;", fgettext("Register"), "]", + "button[4.75,0;2.5,0.8;dlg_register_cancel;", fgettext("Cancel"), "]", + "container_end[]", + }) + + return table.concat(retval, "") +end + +-------------------------------------------------------------------------------- +local function register_buttonhandler(this, fields) + this.data.name = fields.name + this.data.error = nil + + if fields.dlg_register_confirm or fields.key_enter then + if fields.name == "" then + this.data.error = fgettext("Missing name") + return true + end + if fields.password ~= fields.password_2 then + this.data.error = fgettext("Passwords do not match") + return true + end + + gamedata.playername = fields.name + gamedata.password = fields.password + gamedata.address = this.data.address + gamedata.port = this.data.port + gamedata.allow_login_or_register = "register" + gamedata.selected_world = 0 + + assert(gamedata.address and gamedata.port) + + local server = this.data.server + if server then + serverlistmgr.add_favorite(server) + gamedata.servername = server.name + gamedata.serverdescription = server.description + else + gamedata.servername = "" + gamedata.serverdescription = "" + + serverlistmgr.add_favorite({ + address = gamedata.address, + port = gamedata.port, + }) + end + + core.settings:set("name", fields.name) + core.settings:set("address", gamedata.address) + core.settings:set("remote_port", gamedata.port) + + core.start() + end + + if fields["dlg_register_cancel"] then + this:delete() + return true + end + + return false +end + +-------------------------------------------------------------------------------- +function create_register_dialog(address, port, server) + assert(address) + assert(type(port) == "number") + + local retval = dialog_create("dlg_register", + register_formspec, + register_buttonhandler, + nil) + retval.data.address = address + retval.data.port = port + retval.data.server = server + retval.data.name = core.settings:get("name") or "" + return retval +end diff --git a/builtin/mainmenu/dlg_settings_advanced.lua b/builtin/mainmenu/dlg_settings_advanced.lua index 06fd32d84..69562e6a5 100644 --- a/builtin/mainmenu/dlg_settings_advanced.lua +++ b/builtin/mainmenu/dlg_settings_advanced.lua @@ -351,9 +351,9 @@ local function parse_config_file(read_all, parse_mods) local file = io.open(path, "r") if file then if not games_category_initialized then - fgettext_ne("Games") -- not used, but needed for xgettext + fgettext_ne("Content: Games") -- not used, but needed for xgettext table.insert(settings, { - name = "Games", + name = "Content: Games", level = 0, type = "category", }) @@ -378,15 +378,15 @@ local function parse_config_file(read_all, parse_mods) -- Parse mods local mods_category_initialized = false local mods = {} - get_mods(core.get_modpath(), mods) + get_mods(core.get_modpath(), "mods", mods) for _, mod in ipairs(mods) do local path = mod.path .. DIR_DELIM .. FILENAME local file = io.open(path, "r") if file then if not mods_category_initialized then - fgettext_ne("Mods") -- not used, but needed for xgettext + fgettext_ne("Content: Mods") -- not used, but needed for xgettext table.insert(settings, { - name = "Mods", + name = "Content: Mods", level = 0, type = "category", }) @@ -395,6 +395,37 @@ local function parse_config_file(read_all, parse_mods) table.insert(settings, { name = mod.name, + readable_name = mod.title, + level = 1, + type = "category", + }) + + parse_single_file(file, path, read_all, settings, 2, false) + + file:close() + end + end + + -- Parse client mods + local clientmods_category_initialized = false + local clientmods = {} + get_mods(core.get_clientmodpath(), "clientmods", clientmods) + for _, mod in ipairs(clientmods) do + local path = mod.path .. DIR_DELIM .. FILENAME + local file = io.open(path, "r") + if file then + if not clientmods_category_initialized then + fgettext_ne("Client Mods") -- not used, but needed for xgettext + table.insert(settings, { + name = "Client Mods", + level = 0, + type = "category", + }) + clientmods_category_initialized = true + end + + table.insert(settings, { + name = mod.name, level = 1, type = "category", }) @@ -497,44 +528,40 @@ end local function get_current_np_group(setting) local value = core.settings:get_np_group(setting.name) - local t = {} if value == nil then - t = setting.values - else - table.insert(t, value.offset) - table.insert(t, value.scale) - table.insert(t, value.spread.x) - table.insert(t, value.spread.y) - table.insert(t, value.spread.z) - table.insert(t, value.seed) - table.insert(t, value.octaves) - table.insert(t, value.persistence) - table.insert(t, value.lacunarity) - table.insert(t, value.flags) + return setting.values end - return t + local p = "%g" + return { + p:format(value.offset), + p:format(value.scale), + p:format(value.spread.x), + p:format(value.spread.y), + p:format(value.spread.z), + p:format(value.seed), + p:format(value.octaves), + p:format(value.persistence), + p:format(value.lacunarity), + value.flags + } end local function get_current_np_group_as_string(setting) local value = core.settings:get_np_group(setting.name) - local t if value == nil then - t = setting.default - else - t = value.offset .. ", " .. - value.scale .. ", (" .. - value.spread.x .. ", " .. - value.spread.y .. ", " .. - value.spread.z .. "), " .. - value.seed .. ", " .. - value.octaves .. ", " .. - value.persistence .. ", " .. - value.lacunarity - if value.flags ~= "" then - t = t .. ", " .. value.flags - end + return setting.default end - return t + return ("%g, %g, (%g, %g, %g), %g, %g, %g, %g"):format( + value.offset, + value.scale, + value.spread.x, + value.spread.y, + value.spread.z, + value.seed, + value.octaves, + value.persistence, + value.lacunarity + ) .. (value.flags ~= "" and (", " .. value.flags) or "") end local checkboxes = {} -- handle checkboxes events @@ -667,7 +694,7 @@ local function create_change_setting_formspec(dialogdata) elseif setting.type == "v3f" then local val = get_current_value(setting) local v3f = {} - for line in val:gmatch("[+-]?[%d.-e]+") do -- All numeric characters + for line in val:gmatch("[+-]?[%d.+-eE]+") do -- All numeric characters table.insert(v3f, line) end @@ -960,7 +987,7 @@ local function create_settings_formspec(tabview, _, tabdata) local current_level = 0 for _, entry in ipairs(settings) do local name - if not core.settings:get_bool("main_menu_technical_settings") and entry.readable_name then + if not core.settings:get_bool("show_technical_names") and entry.readable_name then name = fgettext_ne(entry.readable_name) else name = entry.name @@ -1001,7 +1028,7 @@ local function create_settings_formspec(tabview, _, tabdata) "button[10,4.9;2,1;btn_edit;" .. fgettext("Edit") .. "]" .. "button[7,4.9;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" .. "checkbox[0,4.3;cb_tech_settings;" .. fgettext("Show technical names") .. ";" - .. dump(core.settings:get_bool("main_menu_technical_settings")) .. "]" + .. dump(core.settings:get_bool("show_technical_names")) .. "]" return formspec end @@ -1084,7 +1111,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) end if fields["cb_tech_settings"] then - core.settings:set("main_menu_technical_settings", fields["cb_tech_settings"]) + core.settings:set("show_technical_names", fields["cb_tech_settings"]) core.settings:write() core.update_formspec(this:get_formspec()) return true diff --git a/builtin/mainmenu/dlg_version_info.lua b/builtin/mainmenu/dlg_version_info.lua new file mode 100644 index 000000000..568fca3f4 --- /dev/null +++ b/builtin/mainmenu/dlg_version_info.lua @@ -0,0 +1,172 @@ +--[[ +Minetest +Copyright (C) 2018-2020 SmallJoker, 2022 rubenwardy + +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. +]] + +if not core.get_http_api then + function check_new_version() + end + return +end + +local function version_info_formspec(data) + local cur_ver = core.get_version() + local title = fgettext("A new $1 version is available", cur_ver.project) + local message = + fgettext("Installed version: $1\nNew version: $2\n" .. + "Visit $3 to find out how to get the newest version and stay up to date" .. + " with features and bugfixes.", + cur_ver.string, data.new_version or "", data.url or "") + + local fs = { + "formspec_version[3]", + "size[12.8,7]", + "style_type[label;textcolor=#0E0]", + "label[0.5,0.8;", core.formspec_escape(title), "]", + "textarea[0.4,1.6;12,3.4;;;", + core.formspec_escape(message), "]", + "container[0.4,5.8]", + "button[0.0,0;4.0,0.8;version_check_visit;", fgettext("Visit website"), "]", + "button[4.5,0;3.5,0.8;version_check_remind;", fgettext("Later"), "]", + "button[8.5.5,0;3.5,0.8;version_check_never;", fgettext("Never"), "]", + "container_end[]", + } + + return table.concat(fs, "") +end + +local function version_info_buttonhandler(this, fields) + if fields.version_check_remind then + -- Erase last known, user will be reminded again at next check + core.settings:set("update_last_known", "") + this:delete() + return true + end + if fields.version_check_never then + core.settings:set("update_last_checked", "disabled") + this:delete() + return true + end + if fields.version_check_visit then + if type(this.data.url) == "string" then + core.open_url(this.data.url) + end + this:delete() + return true + end + + return false +end + +local function create_version_info_dlg(new_version, url) + assert(type(new_version) == "string") + assert(type(url) == "string") + + local retval = dialog_create("version_info", + version_info_formspec, + version_info_buttonhandler, + nil) + + retval.data.new_version = new_version + retval.data.url = url + + return retval +end + +local function get_current_version_code() + -- Format: Major.Minor.Patch + -- Convert to MMMNNNPPP + local cur_string = core.get_version().string + local cur_major, cur_minor, cur_patch = cur_string:match("^(%d+).(%d+).(%d+)") + + if not cur_patch then + core.log("error", "Failed to parse version numbers (invalid tag format?)") + return + end + + return (cur_major * 1000 + cur_minor) * 1000 + cur_patch +end + +local function on_version_info_received(json) + local maintab = ui.find_by_name("maintab") + if maintab.hidden then + -- Another dialog is open, abort. + return + end + + local known_update = tonumber(core.settings:get("update_last_known")) or 0 + + -- Format: MMNNPPP (Major, Minor, Patch) + local new_number = type(json.latest) == "table" and json.latest.version_code + if type(new_number) ~= "number" then + core.log("error", "Failed to read version number (invalid response?)") + return + end + + local cur_number = get_current_version_code() + if new_number <= known_update or new_number < cur_number then + return + end + + -- Also consider updating from 1.2.3-dev to 1.2.3 + if new_number == cur_number and not core.get_version().is_dev then + return + end + + core.settings:set("update_last_known", tostring(new_number)) + + -- Show version info dialog (once) + maintab:hide() + + local version_info_dlg = create_version_info_dlg(json.latest.version, json.latest.url) + version_info_dlg:set_parent(maintab) + version_info_dlg:show() + + ui.update() +end + +function check_new_version() + local url = core.settings:get("update_information_url") + if core.settings:get("update_last_checked") == "disabled" or + url == "" then + -- Never show any updates + return + end + + local time_now = os.time() + local time_checked = tonumber(core.settings:get("update_last_checked")) or 0 + if time_now - time_checked < 2 * 24 * 3600 then + -- Check interval of 2 entire days + return + end + + core.settings:set("update_last_checked", tostring(time_now)) + + core.handle_async(function(params) + local http = core.get_http_api() + return http.fetch_sync(params) + end, { url = url }, function(result) + local json = result.succeeded and core.parse_json(result.data) + if type(json) ~= "table" or not json.latest then + core.log("error", "Failed to read JSON output from " .. url .. + ", status code = " .. result.code) + return + end + + on_version_info_received(json) + end) +end diff --git a/builtin/mainmenu/generate_from_settingtypes.lua b/builtin/mainmenu/generate_from_settingtypes.lua index 43fc57bb9..0f551fbb1 100644 --- a/builtin/mainmenu/generate_from_settingtypes.lua +++ b/builtin/mainmenu/generate_from_settingtypes.lua @@ -31,7 +31,7 @@ local group_format_template = [[ # octaves = %s, # persistence = %s, # lacunarity = %s, -# flags = %s +# flags =%s # } ]] @@ -55,7 +55,11 @@ local function create_minetest_conf_example() end if entry.comment ~= "" then for _, comment_line in ipairs(entry.comment:split("\n", true)) do - insert(result, "# " .. comment_line .. "\n") + if comment_line == "" then + insert(result, "#\n") + else + insert(result, "# " .. comment_line .. "\n") + end end end insert(result, "# type: " .. entry.type) @@ -73,10 +77,14 @@ local function create_minetest_conf_example() end insert(result, "\n") if group_format == true then + local flags = entry.values[10] + if flags ~= "" then + flags = " "..flags + end insert(result, sprintf(group_format_template, entry.name, entry.values[1], entry.values[2], entry.values[3], entry.values[4], entry.values[5], entry.values[6], entry.values[7], entry.values[8], entry.values[9], - entry.values[10])) + flags)) else local append if entry.default ~= "" then @@ -91,7 +99,7 @@ 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 +// It contains 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() {]] @@ -126,4 +134,3 @@ file = assert(io.open("src/settings_translation_file.cpp", "w")) --file = assert(io.open("settings_translation_file.cpp", "w")) file:write(create_translation_file()) file:close() - diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 8e716c2eb..c3a28a570 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -17,9 +17,11 @@ mt_color_grey = "#AAAAAA" mt_color_blue = "#6389FF" +mt_color_lightblue = "#99CCFF" mt_color_green = "#72FF63" mt_color_dark_green = "#25C191" mt_color_orange = "#FF8800" +mt_color_red = "#FF3300" local menupath = core.get_mainmenu_path() local basepath = core.get_builtin_path() @@ -43,7 +45,9 @@ dofile(menupath .. DIR_DELIM .. "dlg_contentstore.lua") dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua") dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua") dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua") +dofile(menupath .. DIR_DELIM .. "dlg_register.lua") dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua") +dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua") local tabs = {} @@ -91,6 +95,7 @@ local function init_globals() -- Create main tabview local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0}) + -- note: size would be 15.5,7.1 in real coordinates mode tv_main:set_autosave_tab(true) tv_main:add(tabs.local_game) @@ -118,8 +123,8 @@ local function init_globals() end ui.set_default("maintab") + check_new_version() tv_main:show() - ui.update() end diff --git a/builtin/mainmenu/pkgmgr.lua b/builtin/mainmenu/pkgmgr.lua index 23f24d617..32a65fd08 100644 --- a/builtin/mainmenu/pkgmgr.lua +++ b/builtin/mainmenu/pkgmgr.lua @@ -78,34 +78,35 @@ local function load_texture_packs(txtpath, retval) for _, item in ipairs(list) do if item ~= "base" then - local name = item - local path = txtpath .. DIR_DELIM .. item .. DIR_DELIM - if path == current_texture_path then - name = fgettext("$1 (Enabled)", name) - end - local conf = Settings(path .. "texture_pack.conf") + local enabled = path == current_texture_path + local title = conf:get("title") or item + + -- list_* is only used if non-nil, else the regular versions are used. retval[#retval + 1] = { name = item, + title = title, + list_name = enabled and fgettext("$1 (Enabled)", item) or nil, + list_title = enabled and fgettext("$1 (Enabled)", title) or nil, author = conf:get("author"), release = tonumber(conf:get("release")) or 0, - list_name = name, type = "txp", path = path, - enabled = path == current_texture_path, + enabled = enabled, } end end end -function get_mods(path,retval,modpack) +function get_mods(path, virtual_path, retval, modpack) local mods = core.get_dir_list(path, true) for _, name in ipairs(mods) do if name:sub(1, 1) ~= "." then - local prefix = path .. DIR_DELIM .. name + local mod_path = path .. DIR_DELIM .. name + local mod_virtual_path = virtual_path .. "/" .. name local toadd = { dir_name = name, parent_dir = path, @@ -114,18 +115,18 @@ function get_mods(path,retval,modpack) -- Get config file local mod_conf - local modpack_conf = io.open(prefix .. DIR_DELIM .. "modpack.conf") + local modpack_conf = io.open(mod_path .. DIR_DELIM .. "modpack.conf") if modpack_conf then toadd.is_modpack = true modpack_conf:close() - mod_conf = Settings(prefix .. DIR_DELIM .. "modpack.conf"):to_table() + mod_conf = Settings(mod_path .. DIR_DELIM .. "modpack.conf"):to_table() if mod_conf.name then name = mod_conf.name toadd.is_name_explicit = true end else - mod_conf = Settings(prefix .. DIR_DELIM .. "mod.conf"):to_table() + mod_conf = Settings(mod_path .. DIR_DELIM .. "mod.conf"):to_table() if mod_conf.name then name = mod_conf.name toadd.is_name_explicit = true @@ -134,14 +135,16 @@ function get_mods(path,retval,modpack) -- Read from config toadd.name = name + toadd.title = mod_conf.title toadd.author = mod_conf.author toadd.release = tonumber(mod_conf.release) or 0 - toadd.path = prefix + toadd.path = mod_path + toadd.virtual_path = mod_virtual_path toadd.type = "mod" -- Check modpack.txt -- Note: modpack.conf is already checked above - local modpackfile = io.open(prefix .. DIR_DELIM .. "modpack.txt") + local modpackfile = io.open(mod_path .. DIR_DELIM .. "modpack.txt") if modpackfile then modpackfile:close() toadd.is_modpack = true @@ -153,7 +156,7 @@ function get_mods(path,retval,modpack) elseif toadd.is_modpack then toadd.type = "modpack" toadd.is_modpack = true - get_mods(prefix, retval, name) + get_mods(mod_path, mod_virtual_path, retval, name) end end end @@ -334,7 +337,7 @@ function pkgmgr.identify_modname(modpath,filename) return nil end -------------------------------------------------------------------------------- -function pkgmgr.render_packagelist(render_list) +function pkgmgr.render_packagelist(render_list, use_technical_names, with_error) if not render_list then if not pkgmgr.global_mods then pkgmgr.refresh_globals() @@ -346,31 +349,75 @@ function pkgmgr.render_packagelist(render_list) local retval = {} for i, v in ipairs(list) do local color = "" + local icon = 0 + local error = with_error and with_error[v.virtual_path] + local function update_error(val) + if val and (not error or (error.type == "warning" and val.type == "error")) then + error = val + end + end + if v.is_modpack then local rawlist = render_list:get_raw_list() color = mt_color_dark_green - for j = 1, #rawlist, 1 do - if rawlist[j].modpack == list[i].name and - not rawlist[j].enabled then - -- Modpack not entirely enabled so showing as grey - color = mt_color_grey - break + for j = 1, #rawlist do + if rawlist[j].modpack == list[i].name then + if with_error then + update_error(with_error[rawlist[j].virtual_path]) + end + + if rawlist[j].enabled then + icon = 1 + else + -- Modpack not entirely enabled so showing as grey + color = mt_color_grey + end end end elseif v.is_game_content or v.type == "game" then + icon = 1 color = mt_color_blue + + local rawlist = render_list:get_raw_list() + if v.type == "game" and with_error then + for j = 1, #rawlist do + if rawlist[j].is_game_content then + update_error(with_error[rawlist[j].virtual_path]) + end + end + end elseif v.enabled or v.type == "txp" then + icon = 1 color = mt_color_green end + if error then + if error.type == "warning" then + color = mt_color_orange + icon = 2 + else + color = mt_color_red + icon = 3 + end + end + retval[#retval + 1] = color if v.modpack ~= nil or v.loc == "game" then retval[#retval + 1] = "1" else retval[#retval + 1] = "0" end - retval[#retval + 1] = core.formspec_escape(v.list_name or v.name) + + if with_error then + retval[#retval + 1] = icon + end + + if use_technical_names then + retval[#retval + 1] = core.formspec_escape(v.list_name or v.name) + else + retval[#retval + 1] = core.formspec_escape(v.list_title or v.list_name or v.title or v.name) + end end return table.concat(retval, ",") @@ -397,6 +444,14 @@ function pkgmgr.is_modpack_entirely_enabled(data, name) return true end +local function disable_all_by_name(list, name, except) + for i=1, #list do + if list[i].name == name and list[i] ~= except then + list[i].enabled = false + end + end +end + ---------- toggles or en/disables a mod or modpack and its dependencies -------- local function toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod) if not mod.is_modpack then @@ -405,13 +460,16 @@ local function toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mo toset = not mod.enabled end if mod.enabled ~= toset then - mod.enabled = toset toggled_mods[#toggled_mods+1] = mod.name end if toset then -- Mark this mod for recursive dependency traversal enabled_mods[mod.name] = true + + -- Disable other mods with the same name + disable_all_by_name(list, mod.name, mod) end + mod.enabled = toset else -- Toggle or en/disable every mod in the modpack, -- interleaved unsupported @@ -472,6 +530,7 @@ function pkgmgr.enable_mod(this, toset) end end end + -- If sp is 0, every dependency is already activated while sp > 0 do local name = to_enable[sp] @@ -483,8 +542,8 @@ function pkgmgr.enable_mod(this, toset) if not mod_to_enable then core.log("warning", "Mod dependency \"" .. name .. "\" not found!") - else - if not mod_to_enable.enabled then + elseif not mod_to_enable.is_game_content then + if not mod_to_enable.enabled then mod_to_enable.enabled = true toggled_mods[#toggled_mods+1] = mod_to_enable.name end @@ -626,6 +685,8 @@ function pkgmgr.install_dir(type, path, basename, targetpath) else targetpath = core.get_gamepath() .. DIR_DELIM .. basename end + else + error("basefolder didn't return a recognised type, this shouldn't happen") end -- Copy it @@ -652,13 +713,14 @@ function pkgmgr.preparemodlist(data) --read global mods local modpaths = core.get_modpaths() - for _, modpath in ipairs(modpaths) do - get_mods(modpath, global_mods) + for key, modpath in pairs(modpaths) do + get_mods(modpath, key, global_mods) end for i=1,#global_mods,1 do global_mods[i].type = "mod" global_mods[i].loc = "global" + global_mods[i].enabled = false retval[#retval + 1] = global_mods[i] end @@ -671,7 +733,7 @@ function pkgmgr.preparemodlist(data) retval[#retval + 1] = { type = "game", is_game_content = true, - name = fgettext("$1 mods", gamespec.name), + name = fgettext("$1 mods", gamespec.title), path = gamespec.path } end @@ -692,22 +754,37 @@ function pkgmgr.preparemodlist(data) DIR_DELIM .. "world.mt" local worldfile = Settings(filename) - - for key,value in pairs(worldfile:to_table()) do + for key, value in pairs(worldfile:to_table()) do if key:sub(1, 9) == "load_mod_" then key = key:sub(10) - local element = nil - for i=1,#retval,1 do + local mod_found = false + + local fallback_found = false + local fallback_mod = nil + + for i=1, #retval do if retval[i].name == key and - not retval[i].is_modpack then - element = retval[i] - break + not retval[i].is_modpack then + if core.is_yes(value) or retval[i].virtual_path == value then + retval[i].enabled = true + mod_found = true + break + elseif fallback_found then + -- Only allow fallback if only one mod matches + fallback_mod = nil + else + fallback_found = true + fallback_mod = retval[i] + end end end - if element ~= nil then - element.enabled = value ~= "false" and value ~= "nil" and value - else - core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found") + + if not mod_found then + if fallback_mod and value:find("/") then + fallback_mod.enabled = true + else + core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found") + end end end end @@ -801,7 +878,7 @@ function pkgmgr.get_game_mods(gamespec, retval) if gamespec ~= nil and gamespec.gamemods_path ~= nil and gamespec.gamemods_path ~= "" then - get_mods(gamespec.gamemods_path, retval) + get_mods(gamespec.gamemods_path, ("games/%s/mods"):format(gamespec.id), retval) end end @@ -837,10 +914,10 @@ end function pkgmgr.gamelist() local retval = "" if #pkgmgr.games > 0 then - retval = retval .. core.formspec_escape(pkgmgr.games[1].name) + retval = retval .. core.formspec_escape(pkgmgr.games[1].title) for i=2,#pkgmgr.games,1 do - retval = retval .. "," .. core.formspec_escape(pkgmgr.games[i].name) + retval = retval .. "," .. core.formspec_escape(pkgmgr.games[i].title) end end return retval diff --git a/builtin/mainmenu/tab_about.lua b/builtin/mainmenu/tab_about.lua index ba258fd2d..a84ebce3f 100644 --- a/builtin/mainmenu/tab_about.lua +++ b/builtin/mainmenu/tab_about.lua @@ -15,48 +15,46 @@ --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --------------------------------------------------------------------------------- +-- https://github.com/orgs/minetest/teams/engine/members local core_developers = { - "Perttu Ahola (celeron55) <celeron55@gmail.com>", + "Perttu Ahola (celeron55) <celeron55@gmail.com> [Project founder]", "sfan5 <sfan5@live.de>", - "Nathanaël Courant (Nore/Ekdohibs) <nore@mesecons.net>", + "ShadowNinja <shadowninja@minetest.net>", + "Nathanaëlle Courant (Nore/Ekdohibs) <nore@mesecons.net>", "Loic Blot (nerzhul/nrz) <loic.blot@unix-experience.fr>", - "paramat", "Andrew Ward (rubenwardy) <rw@rubenwardy.com>", "Krock/SmallJoker <mk939@ymail.com>", "Lars Hofhansl <larsh@apache.org>", - "Pierre-Yves Rollo <dev@pyrollo.com>", "v-rob <robinsonvincent89@gmail.com>", "hecks", "Hugues Ross <hugues.ross@gmail.com>", "Dmitry Kostenko (x2048) <codeforsmile@gmail.com>", } +local core_team = { + "Zughy [Issue triager]", +} + -- For updating active/previous contributors, see the script in ./util/gather_git_credits.py local active_contributors = { - "Wuzzy [I18n for builtin, liquid features, fixes]", - "Zughy [Various features and fixes]", - "numzero [Graphics and rendering]", - "Desour [Internal fixes, Clipboard on X11]", - "Lars Müller [Various internal fixes]", - "JosiahWI [CMake, cleanups and fixes]", - "HybridDog [builtin, documentation]", - "Jude Melton-Houghton [Database implementation]", - "savilli [Fixes]", + "Wuzzy [Features, translations, devtest]", + "Lars Müller [Lua optimizations and fixes]", + "Jude Melton-Houghton [Optimizations, bugfixes]", + "paradust7 [Performance, fixes, Irrlicht refactoring]", + "Desour [Fixes]", + "ROllerozxa [Main menu]", + "savilli [Bugfixes]", + "Lexi Hale [Particlespawner animation]", "Liso [Shadow Mapping]", - "MoNTE48 [Build fix]", - "Jean-Patrick Guerrero (kilbith) [Fixes]", - "ROllerozxa [Code cleanups]", - "Lejo [bitop library integration]", - "LoneWolfHT [Build fixes]", + "JosiahWI [Fixes, build system]", + "numzero [Graphics and rendering]", + "HybridDog [Fixes]", "NeroBurner [Joystick]", - "Elias Fleckenstein [Internal fixes]", - "David CARLIER [Unix & Haiku build fixes]", "pecksin [Clickable web links]", - "srfqi [Android & rendering fixes]", - "EvidenceB [Formspec]", + "Daroc Alden [Fixes]", + "Jean-Patrick Guerrero (kilbith) [Fixes]", } local previous_core_developers = { @@ -71,33 +69,50 @@ local previous_core_developers = { "Ryan Kwolek (kwolekr) <kwolekr@minetest.net>", "sapier", "Zeno", - "ShadowNinja <shadowninja@minetest.net>", "Auke Kok (sofar) <sofar@foo-projects.org>", "Aaron Suen <warr1024@gmail.com>", + "paramat", + "Pierre-Yves Rollo <dev@pyrollo.com>", } local previous_contributors = { - "Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net> [Minetest Logo]", + "Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net> [Minetest logo]", "red-001 <red-001@outlook.ie>", "Giuseppe Bilotta", + "ClobberXD", "Dániel Juhász (juhdanad) <juhdanad@gmail.com>", "MirceaKitsune <mirceakitsune@gmail.com>", + "MoNTE48", "Constantin Wenger (SpeedProg)", "Ciaran Gultnieks (CiaranG)", "Paul Ouellette (pauloue)", "stujones11", + "srifqi", "Rogier <rogier777@gmail.com>", "Gregory Currie (gregorycu)", "JacobF", - "Jeija <jeija@mesecons.net> [HTTP, particles]", + "Jeija <jeija@mesecons.net>", } -local function buildCreditList(source) +local function prepare_credits(dest, source) + for _, s in ipairs(source) do + -- if there's text inside brackets make it gray-ish + s = s:gsub("%[.-%]", core.colorize("#aaa", "%1")) + dest[#dest+1] = s + end +end + +local function build_hacky_list(items, spacing) + spacing = spacing or 0.5 + local y = spacing / 2 local ret = {} - for i = 1, #source do - ret[i] = core.formspec_escape(source[i]) + for _, item in ipairs(items) do + if item ~= "" then + ret[#ret+1] = ("label[0,%f;%s]"):format(y, core.formspec_escape(item)) + end + y = y + spacing end - return table.concat(ret, ",,") + return table.concat(ret, ""), y end return { @@ -106,42 +121,73 @@ return { cbf_formspec = function(tabview, name, tabdata) local logofile = defaulttexturedir .. "logo.png" local version = core.get_version() - local fs = "image[0.75,0.5;2.2,2.2;" .. core.formspec_escape(logofile) .. "]" .. + + local credit_list = {} + table.insert_all(credit_list, { + core.colorize("#ff0", fgettext("Core Developers")) + }) + prepare_credits(credit_list, core_developers) + table.insert_all(credit_list, { + "", + core.colorize("#ff0", fgettext("Core Team")) + }) + prepare_credits(credit_list, core_team) + table.insert_all(credit_list, { + "", + core.colorize("#ff0", fgettext("Active Contributors")) + }) + prepare_credits(credit_list, active_contributors) + table.insert_all(credit_list, { + "", + core.colorize("#ff0", fgettext("Previous Core Developers")) + }) + prepare_credits(credit_list, previous_core_developers) + table.insert_all(credit_list, { + "", + core.colorize("#ff0", fgettext("Previous Contributors")) + }) + prepare_credits(credit_list, previous_contributors) + local credit_fs, scroll_height = build_hacky_list(credit_list) + -- account for the visible portion + scroll_height = math.max(0, scroll_height - 6.9) + + local fs = "image[1.5,0.6;2.5,2.5;" .. core.formspec_escape(logofile) .. "]" .. "style[label_button;border=false]" .. - "button[0.5,2;2.5,2;label_button;" .. version.project .. " " .. version.string .. "]" .. - "button[0.75,2.75;2,2;homepage;minetest.net]" .. - "tablecolumns[color;text]" .. - "tableoptions[background=#00000000;highlight=#00000000;border=false]" .. - "table[3.5,-0.25;8.5,6.05;list_credits;" .. - "#FFFF00," .. fgettext("Core Developers") .. ",," .. - buildCreditList(core_developers) .. ",,," .. - "#FFFF00," .. fgettext("Active Contributors") .. ",," .. - buildCreditList(active_contributors) .. ",,," .. - "#FFFF00," .. fgettext("Previous Core Developers") ..",," .. - buildCreditList(previous_core_developers) .. ",,," .. - "#FFFF00," .. fgettext("Previous Contributors") .. ",," .. - buildCreditList(previous_contributors) .. "," .. - ";1]" + "button[0.1,3.4;5.3,0.5;label_button;" .. + core.formspec_escape(version.project .. " " .. version.string) .. "]" .. + "button[1.5,4.1;2.5,0.8;homepage;minetest.net]" .. + "scroll_container[5.5,0.1;9.5,6.9;scroll_credits;vertical;" .. + tostring(scroll_height / 1000) .. "]" .. credit_fs .. + "scroll_container_end[]".. + "scrollbar[15,0.1;0.4,6.9;vertical;scroll_credits;0]" -- Render information - fs = fs .. "label[0.75,4.9;" .. + fs = fs .. "style[label_button2;border=false]" .. + "button[0.1,6;5.3,1;label_button2;" .. fgettext("Active renderer:") .. "\n" .. core.formspec_escape(core.get_screen_info().render_info) .. "]" - if PLATFORM ~= "Android" then + if PLATFORM == "Android" then + fs = fs .. "button[0.5,5.1;4.5,0.8;share_debug;" .. fgettext("Share debug log") .. "]" + else fs = fs .. "tooltip[userdata;" .. fgettext("Opens the directory that contains user-provided worlds, games, mods,\n" .. "and texture packs in a file manager / explorer.") .. "]" - fs = fs .. "button[0,4;3.5,1;userdata;" .. fgettext("Open User Data Directory") .. "]" + fs = fs .. "button[0.5,5.1;4.5,0.8;userdata;" .. fgettext("Open User Data Directory") .. "]" end - return fs + return fs, "size[15.5,7.1,false]real_coordinates[true]" end, cbf_button_handler = function(this, fields, name, tabdata) if fields.homepage then core.open_url("https://www.minetest.net") end + if fields.share_debug then + local path = core.get_user_path() .. DIR_DELIM .. "debug.txt" + core.share_file(path) + end + if fields.userdata then core.open_dir(core.get_user_path()) end diff --git a/builtin/mainmenu/tab_content.lua b/builtin/mainmenu/tab_content.lua index fb7f121f8..5e14d1902 100644 --- a/builtin/mainmenu/tab_content.lua +++ b/builtin/mainmenu/tab_content.lua @@ -51,12 +51,14 @@ local function get_formspec(tabview, name, tabdata) tabdata.selected_pkg = 1 end + local use_technical_names = core.settings:get_bool("show_technical_names") + local retval = "label[0.05,-0.25;".. fgettext("Installed Packages:") .. "]" .. "tablecolumns[color;tree;text]" .. "table[0,0.25;5.1,4.3;pkglist;" .. - pkgmgr.render_packagelist(packages) .. + pkgmgr.render_packagelist(packages, use_technical_names) .. ";" .. tabdata.selected_pkg .. "]" .. "button[0,4.85;5.25,0.5;btn_contentdb;".. fgettext("Browse online content") .. "]" @@ -87,9 +89,17 @@ local function get_formspec(tabview, name, tabdata) desc = info.description end + local title_and_name + if selected_pkg.type == "game" then + title_and_name = selected_pkg.name + else + title_and_name = (selected_pkg.title or selected_pkg.name) .. "\n" .. + core.colorize("#BFBFBF", selected_pkg.name) + end + retval = retval .. "image[5.5,0;3,2;" .. core.formspec_escape(modscreenshot) .. "]" .. - "label[8.25,0.6;" .. core.formspec_escape(selected_pkg.name) .. "]" .. + "label[8.25,0.6;" .. core.formspec_escape(title_and_name) .. "]" .. "box[5.5,2.2;6.15,2.35;#000]" if selected_pkg.type == "mod" then @@ -154,6 +164,9 @@ local function handle_doubleclick(pkg) core.settings:set("texture_path", pkg.path) end packages = nil + + mm_game_theme.init() + mm_game_theme.reset() end end @@ -197,17 +210,17 @@ local function handle_buttons(tabview, fields, tabname, tabdata) return true end - if fields.btn_mod_mgr_use_txp then - local txp = packages:get_list()[tabdata.selected_pkg] - core.settings:set("texture_path", txp.path) - packages = nil - return true - end - + if fields.btn_mod_mgr_use_txp or fields.btn_mod_mgr_disable_txp then + local txp_path = "" + if fields.btn_mod_mgr_use_txp then + txp_path = packages:get_list()[tabdata.selected_pkg].path + end - if fields.btn_mod_mgr_disable_txp then - core.settings:set("texture_path", "") + core.settings:set("texture_path", txp_path) packages = nil + + mm_game_theme.init() + mm_game_theme.reset() return true end diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index e77c6f04d..f8de10db6 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -16,7 +16,6 @@ --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -local enable_gamebar = PLATFORM ~= "Android" local current_game, singleplayer_refresh_gamebar local valid_disabled_settings = { ["enable_damage"]=true, @@ -24,95 +23,88 @@ local valid_disabled_settings = { ["enable_server"]=true, } -if enable_gamebar then - -- Currently chosen game in gamebar for theming and filtering - function current_game() - local last_game_id = core.settings:get("menu_last_game") - local game = pkgmgr.find_by_gameid(last_game_id) +-- Currently chosen game in gamebar for theming and filtering +function current_game() + local last_game_id = core.settings:get("menu_last_game") + local game = pkgmgr.find_by_gameid(last_game_id) - return game - end + return game +end - -- Apply menu changes from given game - function apply_game(game) - core.set_topleft_text(game.name) - core.settings:set("menu_last_game", game.id) - menudata.worldlist:set_filtercriteria(game.id) +-- Apply menu changes from given game +function apply_game(game) + core.set_topleft_text(game.name) + core.settings:set("menu_last_game", game.id) + menudata.worldlist:set_filtercriteria(game.id) - mm_game_theme.update("singleplayer", game) -- this refreshes the formspec + mm_game_theme.update("singleplayer", game) -- this refreshes the formspec - local index = filterlist.get_current_index(menudata.worldlist, - tonumber(core.settings:get("mainmenu_last_selected_world"))) - if not index or index < 1 then - local selected = core.get_textlist_index("sp_worlds") - if selected ~= nil and selected < #menudata.worldlist:get_list() then - index = selected - else - index = #menudata.worldlist:get_list() - end + local index = filterlist.get_current_index(menudata.worldlist, + tonumber(core.settings:get("mainmenu_last_selected_world"))) + if not index or index < 1 then + local selected = core.get_textlist_index("sp_worlds") + if selected ~= nil and selected < #menudata.worldlist:get_list() then + index = selected + else + index = #menudata.worldlist:get_list() end - menu_worldmt_legacy(index) end + menu_worldmt_legacy(index) +end - function singleplayer_refresh_gamebar() +function singleplayer_refresh_gamebar() - local old_bar = ui.find_by_name("game_button_bar") - if old_bar ~= nil then - old_bar:delete() + local old_bar = ui.find_by_name("game_button_bar") + if old_bar ~= nil then + old_bar:delete() + end + + local function game_buttonbar_button_handler(fields) + if fields.game_open_cdb then + local maintab = ui.find_by_name("maintab") + local dlg = create_store_dlg("game") + dlg:set_parent(maintab) + maintab:hide() + dlg:show() + return true end - local function game_buttonbar_button_handler(fields) - if fields.game_open_cdb then - local maintab = ui.find_by_name("maintab") - local dlg = create_store_dlg("game") - dlg:set_parent(maintab) - maintab:hide() - dlg:show() + for _, game in ipairs(pkgmgr.games) do + if fields["game_btnbar_" .. game.id] then + apply_game(game) return true end - - for _, game in ipairs(pkgmgr.games) do - if fields["game_btnbar_" .. game.id] then - apply_game(game) - return true - end - end end + end - local btnbar = buttonbar_create("game_button_bar", - game_buttonbar_button_handler, - {x=-0.3,y=5.9}, "horizontal", {x=12.4,y=1.15}) + local btnbar = buttonbar_create("game_button_bar", + game_buttonbar_button_handler, + {x=-0.3,y=5.9}, "horizontal", {x=12.4,y=1.15}) - for _, game in ipairs(pkgmgr.games) do - local btn_name = "game_btnbar_" .. game.id + for _, game in ipairs(pkgmgr.games) do + local btn_name = "game_btnbar_" .. game.id - local image = nil - local text = nil - local tooltip = core.formspec_escape(game.name) + local image = nil + local text = nil + local tooltip = core.formspec_escape(game.title) - if (game.menuicon_path or "") ~= "" then - image = core.formspec_escape(game.menuicon_path) - else - local part1 = game.id:sub(1,5) - local part2 = game.id:sub(6,10) - local part3 = game.id:sub(11) + if (game.menuicon_path or "") ~= "" then + image = core.formspec_escape(game.menuicon_path) + else + local part1 = game.id:sub(1,5) + local part2 = game.id:sub(6,10) + local part3 = game.id:sub(11) - text = part1 .. "\n" .. part2 - if part3 ~= "" then - text = text .. "\n" .. part3 - end + text = part1 .. "\n" .. part2 + if part3 ~= "" then + text = text .. "\n" .. part3 end - btnbar:add_button(btn_name, text, image, tooltip) end - - local plus_image = core.formspec_escape(defaulttexturedir .. "plus.png") - btnbar:add_button("game_open_cdb", "", plus_image, fgettext("Install games from ContentDB")) - end -else - -- Currently chosen game in gamebar: no gamebar -> no "current" game - function current_game() - return nil + btnbar:add_button(btn_name, text, image, tooltip) end + + local plus_image = core.formspec_escape(defaulttexturedir .. "plus.png") + btnbar:add_button("game_open_cdb", "", plus_image, fgettext("Install games from ContentDB")) end local function get_disabled_settings(game) @@ -187,7 +179,7 @@ local function get_formspec(tabview, name, tabdata) damage .. host .. "textlist[3.9,0.4;7.9,3.45;sp_worlds;" .. - menu_render_worldlist(not enable_gamebar) .. + menu_render_worldlist() .. ";" .. index .. "]" if core.settings:get_bool("enable_server") and disabled_settings["enable_server"] == nil then @@ -324,7 +316,7 @@ local function main_button_handler(this, fields, name, tabdata) end if fields["world_create"] ~= nil then - local create_world_dlg = create_create_world_dlg(enable_gamebar) + local create_world_dlg = create_create_world_dlg() create_world_dlg:set_parent(this) this:hide() create_world_dlg:show() @@ -371,26 +363,23 @@ local function main_button_handler(this, fields, name, tabdata) end end -local on_change -if enable_gamebar then - function on_change(type, old_tab, new_tab) - if (type == "ENTER") then - local game = current_game() - if game then - apply_game(game) - end +local function on_change(type, old_tab, new_tab) + if (type == "ENTER") then + local game = current_game() + if game then + apply_game(game) + end - singleplayer_refresh_gamebar() - ui.find_by_name("game_button_bar"):show() - else - menudata.worldlist:set_filtercriteria(nil) - local gamebar = ui.find_by_name("game_button_bar") - if gamebar then - gamebar:hide() - end - core.set_topleft_text("") - mm_game_theme.update(new_tab,nil) + singleplayer_refresh_gamebar() + ui.find_by_name("game_button_bar"):show() + else + menudata.worldlist:set_filtercriteria(nil) + local gamebar = ui.find_by_name("game_button_bar") + if gamebar then + gamebar:hide() end + core.set_topleft_text("") + mm_game_theme.update(new_tab,nil) end end diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index fb7409864..899f30bd1 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -87,27 +87,34 @@ local function get_formspec(tabview, name, tabdata) "field[4.25,0.5;1.25,0.75;te_port;;" .. core.formspec_escape(core.settings:get("remote_port")) .. "]" .. - -- Name / Password - "label[0.25,1.55;" .. fgettext("Name") .. "]" .. - "label[3,1.55;" .. fgettext("Password") .. "]" .. - "field[0.25,1.75;2.75,0.75;te_name;;" .. - core.formspec_escape(core.settings:get("name")) .. "]" .. - "pwdfield[3,1.75;2.5,0.75;te_pwd;]" .. - -- Description Background - "label[0.25,2.75;" .. fgettext("Server Description") .. "]" .. - "box[0.25,3;5.25,2.75;#999999]".. + "label[0.25,1.6;" .. fgettext("Server Description") .. "]" .. + "box[0.25,1.85;5.25,2.7;#999999]".. + + -- Name / Password + "container[0,4.8]" .. + "label[0.25,0;" .. fgettext("Name") .. "]" .. + "label[3,0;" .. fgettext("Password") .. "]" .. + "field[0.25,0.2;2.625,0.75;te_name;;" .. core.formspec_escape(core.settings:get("name")) .. "]" .. + "pwdfield[2.875,0.2;2.625,0.75;te_pwd;]" .. + "container_end[]" .. -- Connect - "button[3,6;2.5,0.75;btn_mp_connect;" .. fgettext("Connect") .. "]" + "button[3,6;2.5,0.75;btn_mp_login;" .. fgettext("Login") .. "]" + + if core.settings:get_bool("enable_split_login_register") then + retval = retval .. "button[0.25,6;2.5,0.75;btn_mp_register;" .. fgettext("Register") .. "]" + end if tabdata.selected then if gamedata.fav then - retval = retval .. "button[0.25,6;2.5,0.75;btn_delete_favorite;" .. - fgettext("Del. Favorite") .. "]" + retval = retval .. "tooltip[btn_delete_favorite;" .. fgettext("Remove favorite") .. "]" + retval = retval .. "style[btn_delete_favorite;padding=6]" + retval = retval .. "image_button[5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir .. + "server_favorite_delete.png") .. ";btn_delete_favorite;]" end if gamedata.serverdescription then - retval = retval .. "textarea[0.25,3;5.25,2.75;;;" .. + retval = retval .. "textarea[0.25,1.85;5.2,2.75;;;" .. core.formspec_escape(gamedata.serverdescription) .. "]" end end @@ -339,12 +346,15 @@ local function main_button_handler(tabview, fields, name, tabdata) return true end - if (fields.btn_mp_connect or fields.key_enter) + if (fields.btn_mp_login or fields.key_enter) and fields.te_address ~= "" and fields.te_port then gamedata.playername = fields.te_name gamedata.password = fields.te_pwd gamedata.address = fields.te_address gamedata.port = tonumber(fields.te_port) + + local enable_split_login_register = core.settings:get_bool("enable_split_login_register") + gamedata.allow_login_or_register = enable_split_login_register and "login" or "any" gamedata.selected_world = 0 local idx = core.get_table_index("servers") @@ -381,6 +391,25 @@ local function main_button_handler(tabview, fields, name, tabdata) return true end + if fields.btn_mp_register and fields.te_address ~= "" and fields.te_port then + local idx = core.get_table_index("servers") + local server = idx and tabdata.lookup[idx] + if server and (server.address ~= fields.te_address or server.port ~= tonumber(fields.te_port)) then + server = nil + end + + if server and not is_server_protocol_compat_or_error( + server.proto_min, server.proto_max) then + return true + end + + local dlg = create_register_dialog(fields.te_address, tonumber(fields.te_port), server) + dlg:set_parent(tabview) + tabview:hide() + dlg:show() + return true + end + return false end diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index 700b7390f..21c77aa8e 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -50,7 +50,7 @@ local labels = { fgettext("Low"), fgettext("Medium"), fgettext("High"), - fgettext("Ultra High") + fgettext("Very High") } } @@ -160,7 +160,7 @@ local function formspec(tabview, name, tabdata) .. getSettingIndex.NodeHighlighting() .. "]" .. "dropdown[0.25,3.6;3.5;dd_leaves_style;" .. dd_options.leaves[1] .. ";" .. getSettingIndex.Leaves() .. "]" .. - "box[4,0;3.75,4.5;#999999]" .. + "box[4,0;3.75,4.9;#999999]" .. "label[4.25,0.1;" .. fgettext("Texturing:") .. "]" .. "dropdown[4.25,0.55;3.5;dd_filters;" .. dd_options.filters[1] .. ";" .. getSettingIndex.Filter() .. "]" .. @@ -169,9 +169,6 @@ local function formspec(tabview, name, tabdata) "label[4.25,2.15;" .. fgettext("Antialiasing:") .. "]" .. "dropdown[4.25,2.6;3.5;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";" .. getSettingIndex.Antialiasing() .. "]" .. - "label[4.25,3.45;" .. fgettext("Screen:") .. "]" .. - "checkbox[4.25,3.6;cb_autosave_screensize;" .. fgettext("Autosave Screen Size") .. ";" - .. dump(core.settings:get_bool("autosave_screensize")) .. "]" .. "box[8,0;3.75,4.5;#999999]" local video_driver = core.settings:get("video_driver") @@ -203,10 +200,15 @@ local function formspec(tabview, name, tabdata) if core.settings:get("touchscreen_threshold") ~= nil then tab_string = tab_string .. - "label[4.3,4.2;" .. fgettext("Touchthreshold: (px)") .. "]" .. - "dropdown[4.25,4.65;3.5;dd_touchthreshold;0,10,20,30,40,50;" .. + "label[4.25,3.5;" .. fgettext("Touch threshold (px):") .. "]" .. + "dropdown[4.25,3.95;3.5;dd_touchthreshold;0,10,20,30,40,50;" .. ((tonumber(core.settings:get("touchscreen_threshold")) / 10) + 1) .. - "]box[4.0,4.5;3.75,1.0;#999999]" + "]" + else + tab_string = tab_string .. + "label[4.25,3.65;" .. fgettext("Screen:") .. "]" .. + "checkbox[4.25,3.9;cb_autosave_screensize;" .. fgettext("Autosave Screen Size") .. ";" + .. dump(core.settings:get_bool("autosave_screensize")) .. "]" end if shaders_enabled then @@ -219,9 +221,18 @@ local function formspec(tabview, name, tabdata) .. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" .. "checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" .. dump(core.settings:get_bool("enable_waving_plants")) .. "]" - --"label[8.25,3.0;" .. fgettext("Dynamic shadows: ") .. "]" .. - --"dropdown[8.25,3.5;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";" - -- .. getSettingIndex.ShadowMapping() .. "]" + + if video_driver == "opengl" then + tab_string = tab_string .. + "label[8.25,2.8;" .. fgettext("Dynamic shadows:") .. "]" .. + "label[8.25,3.2;" .. fgettext("(game support required)") .. "]" .. + "dropdown[8.25,3.7;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";" + .. getSettingIndex.ShadowMapping() .. "]" + else + tab_string = tab_string .. + "label[8.38,2.7;" .. core.colorize("#888888", + fgettext("Dynamic shadows")) .. "]" + end else tab_string = tab_string .. "label[8.38,0.7;" .. core.colorize("#888888", @@ -231,9 +242,9 @@ local function formspec(tabview, name, tabdata) "label[8.38,1.7;" .. core.colorize("#888888", fgettext("Waving Leaves")) .. "]" .. "label[8.38,2.2;" .. core.colorize("#888888", - fgettext("Waving Plants")) .. "]" - --"label[8.38,2.7;" .. core.colorize("#888888", - -- fgettext("Dynamic shadows")) .. "]" + fgettext("Waving Plants")) .. "]".. + "label[8.38,2.7;" .. core.colorize("#888888", + fgettext("Dynamic shadows")) .. "]" end return tab_string @@ -364,11 +375,11 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) core.settings:set("enable_dynamic_shadows", "false") else local shadow_presets = { - [2] = { 80, 512, "true", 0, "false" }, - [3] = { 120, 1024, "true", 1, "false" }, - [4] = { 350, 2048, "true", 1, "false" }, - [5] = { 350, 2048, "true", 2, "true" }, - [6] = { 450, 4096, "true", 2, "true" }, + [2] = { 62, 512, "true", 0, "false" }, + [3] = { 93, 1024, "true", 0, "false" }, + [4] = { 140, 2048, "true", 1, "false" }, + [5] = { 210, 4096, "true", 2, "true" }, + [6] = { 300, 8192, "true", 2, "true" }, } local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])] if s then diff --git a/builtin/mainmenu/tests/serverlistmgr_spec.lua b/builtin/mainmenu/tests/serverlistmgr_spec.lua index a091959fb..ab7a6c60c 100644 --- a/builtin/mainmenu/tests/serverlistmgr_spec.lua +++ b/builtin/mainmenu/tests/serverlistmgr_spec.lua @@ -1,4 +1,5 @@ _G.core = {} +_G.vector = {metatable = {}} _G.unpack = table.unpack _G.serverlistmgr = {} diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 01bebe4cc..52b4b4d9d 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -63,26 +63,15 @@ # There shouldn't be too much settings per category; settings that shouldn't be # modified by the "average user" should be in (sub-)categories called "Advanced". + [Controls] + +[*General] + # If enabled, you can place blocks at the position (feet + eye level) where you stand. # This is helpful when working with nodeboxes in small areas. enable_build_where_you_stand (Build inside player) bool false -# Player is able to fly without being affected by gravity. -# This requires the "fly" privilege on the server. -free_move (Flying) bool false - -# If enabled, makes move directions relative to the player's pitch when flying or swimming. -pitch_move (Pitch move mode) bool false - -# Fast movement (via the "Aux1" key). -# This requires the "fast" privilege on the server. -fast_move (Fast movement) bool false - -# If enabled together with fly mode, player is able to fly through solid nodes. -# This requires the "noclip" privilege on the server. -noclip (Noclip) bool false - # Smooths camera when looking around. Also called look or mouse smoothing. # Useful for recording videos. cinematic (Cinematic mode) bool false @@ -93,12 +82,6 @@ camera_smoothing (Camera smoothing) float 0.0 0.0 0.99 # Smooths rotation of camera in cinematic mode. 0 to disable. cinematic_camera_smoothing (Camera smoothing in cinematic mode) float 0.7 0.0 0.99 -# Invert vertical mouse movement. -invert_mouse (Invert mouse) bool false - -# Mouse sensitivity multiplier. -mouse_sensitivity (Mouse sensitivity) float 0.2 - # If enabled, "Aux1" key instead of "Sneak" key is used for climbing down and # descending. aux1_descends (Aux1 key for climbing/descending) bool false @@ -108,11 +91,11 @@ doubletap_jump (Double tap jump for fly) bool false # If disabled, "Aux1" key is used to fly fast if both fly and fast mode are # enabled. -always_fly_fast (Always fly and fast) bool true +always_fly_fast (Always fly fast) bool true # The time in seconds it takes between repeated node placements when holding # the place button. -repeat_place_time (Place repetition interval) float 0.25 0.001 +repeat_place_time (Place repetition interval) float 0.25 0.25 2 # Automatically jump up single-node obstacles. autojump (Automatic jumping) bool false @@ -121,12 +104,15 @@ autojump (Automatic jumping) bool false # Enable this when you dig or place too often by accident. safe_dig_and_place (Safe digging and placing) bool false -# Enable random user input (only used for testing). -random_input (Random input) bool false +[*Keyboard and Mouse] -# Continuous forward movement, toggled by autoforward key. -# Press the autoforward key again or the backwards movement to disable. -continuous_forward (Continuous forward) bool false +# Invert vertical mouse movement. +invert_mouse (Invert mouse) bool false + +# Mouse sensitivity multiplier. +mouse_sensitivity (Mouse sensitivity) float 0.2 0.001 10.0 + +[*Touchscreen] # The length in pixels it takes for touch screen interaction to start. touchscreen_threshold (Touch screen threshold) int 20 0 100 @@ -139,345 +125,179 @@ fixed_virtual_joystick (Fixed virtual joystick) bool false # If enabled, virtual joystick will also tap "Aux1" button when out of main circle. virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false -# Enable joysticks -enable_joysticks (Enable joysticks) bool false - -# The identifier of the joystick to use -joystick_id (Joystick ID) int 0 - -# The type of joystick -joystick_type (Joystick type) enum auto auto,generic,xbox,dragonrise_gamecube - -# 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 0.001 - -# The dead zone of the joystick -joystick_deadzone (Joystick dead zone) int 2048 - -# The sensitivity of the joystick axes for moving the -# in-game 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 - -# Key for moving the player backward. -# Will also disable autoforward, when active. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_backward (Backward key) key KEY_KEY_S - -# Key for moving the player left. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_left (Left key) key KEY_KEY_A - -# Key for moving the player right. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_right (Right key) key KEY_KEY_D - -# Key for jumping. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_jump (Jump key) key KEY_SPACE - -# Key for sneaking. -# Also used for climbing down and descending in water if aux1_descends is disabled. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_sneak (Sneak key) key KEY_LSHIFT - -# Key for digging. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_dig (Dig key) key KEY_LBUTTON - -# Key for placing. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_place (Place key) key KEY_RBUTTON - -# Key for opening the inventory. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_inventory (Inventory key) key KEY_KEY_I - -# Key for moving fast in fast mode. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_aux1 (Aux1 key) key KEY_KEY_E - -# Key for opening the chat window. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_chat (Chat key) key KEY_KEY_T - -# Key for opening the chat window to type commands. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_cmd (Command key) key / - -# Key for opening the chat window to type local commands. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_cmd_local (Command key) key . - -# Key for toggling unlimited view range. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_rangeselect (Range select key) key KEY_KEY_R - -# Key for toggling flying. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_freemove (Fly key) key KEY_KEY_K - -# Key for toggling pitch move mode. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_pitchmove (Pitch move key) key KEY_KEY_P - -# Key for toggling fast mode. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_fastmove (Fast key) key KEY_KEY_J - -# Key for toggling noclip mode. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_noclip (Noclip key) key KEY_KEY_H - -# Key for selecting the next item in the hotbar. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_hotbar_next (Hotbar next key) key KEY_KEY_N - -# Key for selecting the previous item in the hotbar. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_hotbar_previous (Hotbar previous key) key KEY_KEY_B - -# Key for muting the game. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_mute (Mute key) key KEY_KEY_M - -# Key for increasing the volume. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_increase_volume (Inc. volume key) key -# Key for decreasing the volume. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_decrease_volume (Dec. volume key) key +[Graphics and Audio] -# Key for toggling autoforward. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_autoforward (Automatic forward key) key +[*Graphics] -# Key for toggling cinematic mode. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_cinematic (Cinematic mode key) key +[**Screen] -# Key for toggling display of minimap. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_minimap (Minimap key) key KEY_KEY_V - -# Key for taking screenshots. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_screenshot (Screenshot) key KEY_F12 - -# Key for dropping the currently selected item. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_drop (Drop item key) key KEY_KEY_Q - -# Key to use view zoom when possible. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_zoom (View zoom key) key KEY_KEY_Z - -# Key for selecting the first hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot1 (Hotbar slot 1 key) key KEY_KEY_1 - -# Key for selecting the second hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot2 (Hotbar slot 2 key) key KEY_KEY_2 - -# Key for selecting the third hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot3 (Hotbar slot 3 key) key KEY_KEY_3 - -# Key for selecting the fourth hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot4 (Hotbar slot 4 key) key KEY_KEY_4 - -# Key for selecting the fifth hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot5 (Hotbar slot 5 key) key KEY_KEY_5 - -# Key for selecting the sixth hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot6 (Hotbar slot 6 key) key KEY_KEY_6 +# Width component of the initial window size. Ignored in fullscreen mode. +screen_w (Screen width) int 1024 1 65535 -# Key for selecting the seventh hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot7 (Hotbar slot 7 key) key KEY_KEY_7 +# Height component of the initial window size. Ignored in fullscreen mode. +screen_h (Screen height) int 600 1 65535 -# Key for selecting the eighth hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot8 (Hotbar slot 8 key) key KEY_KEY_8 +# Save window size automatically when modified. +autosave_screensize (Autosave screen size) bool true -# Key for selecting the ninth hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot9 (Hotbar slot 9 key) key KEY_KEY_9 +# Fullscreen mode. +fullscreen (Full screen) bool false -# Key for selecting the tenth hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot10 (Hotbar slot 10 key) key KEY_KEY_0 +# Open the pause menu when the window's focus is lost. Does not pause if a formspec is +# open. +pause_on_lost_focus (Pause on lost window focus) bool false -# Key for selecting the 11th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot11 (Hotbar slot 11 key) key +[**FPS] -# Key for selecting the 12th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot12 (Hotbar slot 12 key) key +# If FPS would go higher than this, limit it by sleeping +# to not waste CPU power for no benefit. +fps_max (Maximum FPS) int 60 1 4294967295 -# Key for selecting the 13th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot13 (Hotbar slot 13 key) key +# Vertical screen synchronization. +vsync (VSync) bool false -# Key for selecting the 14th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot14 (Hotbar slot 14 key) key +# Maximum FPS when the window is not focused, or when the game is paused. +fps_max_unfocused (FPS when unfocused or paused) int 20 1 4294967295 -# Key for selecting the 15th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot15 (Hotbar slot 15 key) key +# View distance in nodes. +viewing_range (Viewing range) int 190 20 4000 -# Key for selecting the 16th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot16 (Hotbar slot 16 key) key +# Undersampling is similar to using a lower screen resolution, but it applies +# to the game world only, keeping the GUI intact. +# It should give a significant performance boost at the cost of less detailed image. +# Higher values result in a less detailed image. +undersampling (Undersampling) int 1 1 8 -# Key for selecting the 17th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot17 (Hotbar slot 17 key) key +[**Graphics Effects] -# Key for selecting the 18th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot18 (Hotbar slot 18 key) key +# Makes all liquids opaque +opaque_water (Opaque liquids) bool false -# Key for selecting the 19th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot19 (Hotbar slot 19 key) key +# Leaves style: +# - Fancy: all faces visible +# - Simple: only outer faces, if defined special_tiles are used +# - Opaque: disable transparency +leaves_style (Leaves style) enum fancy fancy,simple,opaque -# Key for selecting the 20th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot20 (Hotbar slot 20 key) key +# Connects glass if supported by node. +connected_glass (Connect glass) bool false -# Key for selecting the 21st hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot21 (Hotbar slot 21 key) key +# Enable smooth lighting with simple ambient occlusion. +# Disable for speed or for different looks. +smooth_lighting (Smooth lighting) bool true -# Key for selecting the 22nd hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot22 (Hotbar slot 22 key) key +# Enables tradeoffs that reduce CPU load or increase rendering performance +# at the expense of minor visual glitches that do not impact game playability. +performance_tradeoffs (Tradeoffs for performance) bool false -# Key for selecting the 23rd hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot23 (Hotbar slot 23 key) key +# Adds particles when digging a node. +enable_particles (Digging particles) bool true -# Key for selecting the 24th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot24 (Hotbar slot 24 key) key +[**3d] -# Key for selecting the 25th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot25 (Hotbar slot 25 key) key +# 3D support. +# Currently supported: +# - none: no 3d output. +# - anaglyph: cyan/magenta color 3d. +# - interlaced: odd/even line based polarisation screen support. +# - topbottom: split screen top/bottom. +# - sidebyside: split screen side by side. +# - crossview: Cross-eyed 3d +# - pageflip: quadbuffer based 3d. +# Note that the interlaced mode requires shaders to be enabled. +3d_mode (3D mode) enum none none,anaglyph,interlaced,topbottom,sidebyside,crossview,pageflip -# Key for selecting the 26th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot26 (Hotbar slot 26 key) key +# Strength of 3D mode parallax. +3d_paralax_strength (3D mode parallax strength) float 0.025 -0.087 0.087 -# Key for selecting the 27th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot27 (Hotbar slot 27 key) key +[**Bobbing] -# Key for selecting the 28th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot28 (Hotbar slot 28 key) key +# Arm inertia, gives a more realistic movement of +# the arm when the camera moves. +arm_inertia (Arm inertia) bool true -# Key for selecting the 29th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot29 (Hotbar slot 29 key) key +# Enable view bobbing and amount of view bobbing. +# For example: 0 for no view bobbing; 1.0 for normal; 2.0 for double. +view_bobbing_amount (View bobbing factor) float 1.0 0.0 7.9 -# Key for selecting the 30th hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot30 (Hotbar slot 30 key) key +# Multiplier for fall bobbing. +# For example: 0 for no view bobbing; 1.0 for normal; 2.0 for double. +fall_bobbing_amount (Fall bobbing factor) float 0.03 0.0 100.0 -# Key for selecting the 31st hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot31 (Hotbar slot 31 key) key +[**Camera] -# Key for selecting the 32nd hotbar slot. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_slot32 (Hotbar slot 32 key) key +# Camera 'near clipping plane' distance in nodes, between 0 and 0.25 +# Only works on GLES platforms. Most users will not need to change this. +# Increasing can reduce artifacting on weaker GPUs. +# 0.1 = Default, 0.25 = Good value for weaker tablets. +near_plane (Near plane) float 0.1 0 0.25 -# Key for toggling the display of the HUD. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_toggle_hud (HUD toggle key) key KEY_F1 +# Field of view in degrees. +fov (Field of view) int 72 45 160 -# Key for toggling the display of chat. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_toggle_chat (Chat toggle key) key KEY_F2 +# Alters the light curve by applying 'gamma correction' to it. +# Higher values make middle and lower light levels brighter. +# Value '1.0' leaves the light curve unaltered. +# This only has significant effect on daylight and artificial +# light, it has very little effect on natural night light. +display_gamma (Light curve gamma) float 1.0 0.33 3.0 -# Key for toggling the display of the large chat console. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_console (Large chat console key) key KEY_F10 +# The strength (darkness) of node ambient-occlusion shading. +# Lower is darker, Higher is lighter. The valid range of values for this +# setting is 0.25 to 4.0 inclusive. If the value is out of range it will be +# set to the nearest valid value. +ambient_occlusion_gamma (Ambient occlusion gamma) float 2.2 0.25 4.0 -# Key for toggling the display of fog. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_toggle_force_fog_off (Fog toggle key) key KEY_F3 +[**Screenshots] -# 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 +# Path to save screenshots at. Can be an absolute or relative path. +# The folder will be created if it doesn't already exist. +screenshot_path (Screenshot folder) path screenshots -# Key for toggling the display of debug info. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_toggle_debug (Debug info toggle key) key KEY_F5 +# Format of screenshots. +screenshot_format (Screenshot format) enum png png,jpg -# Key for toggling the display of the profiler. Used for development. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_toggle_profiler (Profiler toggle key) key KEY_F6 +# Screenshot quality. Only used for JPEG format. +# 1 means worst quality; 100 means best quality. +# Use 0 for default quality. +screenshot_quality (Screenshot quality) int 0 0 100 -# Key for switching between first- and third-person camera. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_camera_mode (Toggle camera mode key) key KEY_KEY_C +[**Node and Entity Highlighting] -# Key for increasing the viewing range. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_increase_viewing_range_min (View range increase key) key + +# Method used to highlight selected object. +node_highlighting (Node highlighting) enum box box,halo,none -# Key for decreasing the viewing range. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_decrease_viewing_range_min (View range decrease key) key - +# Show entity selection boxes +# A restart is required after changing this. +show_entity_selectionbox (Show entity selection boxes) bool false -[Graphics] +# Selection box border color (R,G,B). +selectionbox_color (Selection box color) string (0,0,0) -[*In-Game] +# Width of the selection box lines around nodes. +selectionbox_width (Selection box width) int 2 1 5 -[**Basic] +# Crosshair color (R,G,B). +# Also controls the object crosshair color +crosshair_color (Crosshair color) string (255,255,255) -# Whether name tag backgrounds should be shown by default. -# Mods may still set a background. -show_nametag_backgrounds (Show name tag backgrounds by default) bool true +# Crosshair alpha (opaqueness, between 0 and 255). +# This also applies to the object crosshair. +crosshair_alpha (Crosshair alpha) int 255 0 255 -# Enable vertex buffer objects. -# This should greatly improve graphics performance. -enable_vbo (VBO) bool true +[**Fog] # Whether to fog out the end of the visible area. enable_fog (Fog) bool true -# Leaves style: -# - Fancy: all faces visible -# - Simple: only outer faces, if defined special_tiles are used -# - Opaque: disable transparency -leaves_style (Leaves style) enum fancy fancy,simple,opaque - -# Connects glass if supported by node. -connected_glass (Connect glass) bool false +# Make fog and sky colors depend on daytime (dawn/sunset) and view direction. +directional_colored_fog (Colored fog) bool true -# Enable smooth lighting with simple ambient occlusion. -# Disable for speed or for different looks. -smooth_lighting (Smooth lighting) bool true +# Fraction of the visible distance at which fog starts to be rendered +fog_start (Fog start) float 0.4 0.0 0.99 -# Enables tradeoffs that reduce CPU load or increase rendering performance -# at the expense of minor visual glitches that do not impact game playability. -performance_tradeoffs (Tradeoffs for performance) bool false +[**Clouds] # Clouds are a client side effect. enable_clouds (Clouds) bool true @@ -485,13 +305,7 @@ enable_clouds (Clouds) bool true # Use 3D cloud look instead of flat. enable_3d_clouds (3D clouds) bool true -# Method used to highlight selected object. -node_highlighting (Node highlighting) enum box box,halo,none - -# Adds particles when digging a node. -enable_particles (Digging particles) bool true - -[**Filtering] +[**Filtering and Antialiasing] # Use mipmapping to scale textures. May slightly increase performance, # especially when using a high resolution texture pack. @@ -521,7 +335,7 @@ texture_clean_transparent (Clean transparent textures) bool false # bilinear/trilinear/anisotropic filtering is enabled. # This is also used as the base node texture size for world-aligned # texture autoscaling. -texture_min_size (Minimum texture size) int 64 +texture_min_size (Minimum texture size) int 64 1 32768 # Use multi-sample antialiasing (MSAA) to smooth out block edges. # This algorithm smooths out the 3D viewport while keeping the image sharp, @@ -532,23 +346,15 @@ texture_min_size (Minimum texture size) int 64 # A restart is required after changing this option. fsaa (FSAA) enum 0 0,1,2,4,8,16 -# Undersampling is similar to using a lower screen resolution, but it applies -# to the game world only, keeping the GUI intact. -# It should give a significant performance boost at the cost of less detailed image. -# Higher values result in a less detailed image. -undersampling (Undersampling) int 1 1 8 -[**Shaders] +[*Shaders] # Shaders allow advanced visual effects and may increase performance on some video # cards. # This only works with the OpenGL video backend. enable_shaders (Shaders) bool true -# Path to shader directory. If no path is defined, default location will be used. -shader_path (Shader path) path - -[***Tone Mapping] +[**Tone Mapping] # Enables Hable's 'Uncharted 2' filmic tone mapping. # Simulates the tone curve of photographic film and how this approximates the @@ -556,7 +362,15 @@ shader_path (Shader path) path # enhanced, highlights and shadows are gradually compressed. tone_mapping (Filmic tone mapping) bool false -[***Waving Nodes] +[**Waving Nodes] + +# Set to true to enable waving leaves. +# Requires shaders to be enabled. +enable_waving_leaves (Waving leaves) bool false + +# Set to true to enable waving plants. +# Requires shaders to be enabled. +enable_waving_plants (Waving plants) bool false # Set to true to enable waving liquids (like water). # Requires shaders to be enabled. @@ -578,132 +392,88 @@ water_wave_length (Waving liquids wavelength) float 20.0 0.1 # Requires waving liquids to be enabled. water_wave_speed (Waving liquids wave speed) float 5.0 -# Set to true to enable waving leaves. -# Requires shaders to be enabled. -enable_waving_leaves (Waving leaves) bool false +[**Dynamic shadows] -# Set to true to enable waving plants. +# Set to true to enable Shadow Mapping. # Requires shaders to be enabled. -enable_waving_plants (Waving plants) bool false +enable_dynamic_shadows (Dynamic shadows) bool false -[**Advanced] - -# Arm inertia, gives a more realistic movement of -# the arm when the camera moves. -arm_inertia (Arm inertia) bool true - -# If FPS would go higher than this, limit it by sleeping -# to not waste CPU power for no benefit. -fps_max (Maximum FPS) int 60 1 - -# Maximum FPS when the window is not focused, or when the game is paused. -fps_max_unfocused (FPS when unfocused or paused) int 20 1 +# Set the shadow strength gamma. +# Adjusts the intensity of in-game dynamic shadows. +# Lower value means lighter shadows, higher value means darker shadows. +shadow_strength_gamma (Shadow strength gamma) float 1.0 0.1 10.0 -# Open the pause menu when the window's focus is lost. Does not pause if a formspec is -# open. -pause_on_lost_focus (Pause on lost window focus) bool false - -# View distance in nodes. -viewing_range (Viewing range) int 190 20 4000 - -# Camera 'near clipping plane' distance in nodes, between 0 and 0.25 -# Only works on GLES platforms. Most users will not need to change this. -# Increasing can reduce artifacting on weaker GPUs. -# 0.1 = Default, 0.25 = Good value for weaker tablets. -near_plane (Near plane) float 0.1 0 0.25 - -# Width component of the initial window size. Ignored in fullscreen mode. -screen_w (Screen width) int 1024 1 +# Maximum distance to render shadows. +shadow_map_max_distance (Shadow map max distance in nodes to render shadows) float 120.0 10.0 1000.0 -# Height component of the initial window size. Ignored in fullscreen mode. -screen_h (Screen height) int 600 1 +# Texture size to render the shadow map on. +# This must be a power of two. +# Bigger numbers create better shadows but it is also more expensive. +shadow_map_texture_size (Shadow map texture size) int 1024 128 8192 -# Save window size automatically when modified. -autosave_screensize (Autosave screen size) bool true +# Sets shadow texture quality to 32 bits. +# On false, 16 bits texture will be used. +# This can cause much more artifacts in the shadow. +shadow_map_texture_32bit (Shadow map texture in 32 bits) bool true -# Fullscreen mode. -fullscreen (Full screen) bool false +# Enable Poisson disk filtering. +# On true uses Poisson disk to make "soft shadows". Otherwise uses PCF filtering. +shadow_poisson_filter (Poisson filtering) bool true -# Vertical screen synchronization. -vsync (VSync) bool false +# Define shadow filtering quality. +# This simulates the soft shadows effect by applying a PCF or Poisson disk +# but also uses more resources. +shadow_filters (Shadow filter quality) enum 1 0,1,2 -# Field of view in degrees. -fov (Field of view) int 72 45 160 +# Enable colored shadows. +# On true translucent nodes cast colored shadows. This is expensive. +shadow_map_color (Colored shadows) bool false -# Alters the light curve by applying 'gamma correction' to it. -# Higher values make middle and lower light levels brighter. -# Value '1.0' leaves the light curve unaltered. -# This only has significant effect on daylight and artificial -# light, it has very little effect on natural night light. -display_gamma (Light curve gamma) float 1.0 0.33 3.0 +# Spread a complete update of shadow map over given amount of frames. +# Higher values might make shadows laggy, lower values +# will consume more resources. +# Minimum value: 1; maximum value: 16 +shadow_update_frames (Map shadows update frames) int 8 1 16 -# Gradient of light curve at minimum light level. -# Controls the contrast of the lowest light levels. -lighting_alpha (Light curve low gradient) float 0.0 0.0 3.0 +# Set the soft shadow radius size. +# Lower values mean sharper shadows, bigger values mean softer shadows. +# Minimum value: 1.0; maximum value: 15.0 +shadow_soft_radius (Soft shadow radius) float 5.0 1.0 15.0 -# Gradient of light curve at maximum light level. -# Controls the contrast of the highest light levels. -lighting_beta (Light curve high gradient) float 1.5 0.0 3.0 +# Set the tilt of Sun/Moon orbit in degrees. +# Value of 0 means no tilt / vertical orbit. +# Minimum value: 0.0; maximum value: 60.0 +shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 0.0 60.0 -# Strength of light curve boost. -# The 3 'boost' parameters define a range of the light -# curve that is boosted in brightness. -lighting_boost (Light curve boost) float 0.2 0.0 0.4 +[*Audio] -# Center of light curve boost range. -# Where 0.0 is minimum light level, 1.0 is maximum light level. -lighting_boost_center (Light curve boost center) float 0.5 0.0 1.0 +# Volume of all sounds. +# Requires the sound system to be enabled. +sound_volume (Volume) float 0.7 0.0 1.0 -# Spread of light curve boost range. -# Controls the width of the range to be boosted. -# Standard deviation of the light curve boost Gaussian. -lighting_boost_spread (Light curve boost spread) float 0.2 0.0 0.4 +# Whether to mute sounds. You can unmute sounds at any time, unless the +# sound system is disabled (enable_sound=false). +# In-game, you can toggle the mute state with the mute key or by using the +# pause menu. +mute_sound (Mute sound) bool false -# Path to texture directory. All textures are first searched from here. -texture_path (Texture path) path +[*User Interfaces] -# The rendering back-end. +# Set the language. Leave empty to use the system language. # A restart is required after changing this. -# Note: On Android, stick with OGLES1 if unsure! App may fail to start otherwise. -# On other platforms, OpenGL is recommended. -# Shaders are supported by OpenGL (desktop only) and OGLES2 (experimental) -video_driver (Video driver) enum opengl opengl,ogles1,ogles2 - -# Radius of cloud area stated in number of 64 node cloud squares. -# Values larger than 26 will start to produce sharp cutoffs at cloud area corners. -cloud_radius (Cloud radius) int 12 - -# Enable view bobbing and amount of view bobbing. -# For example: 0 for no view bobbing; 1.0 for normal; 2.0 for double. -view_bobbing_amount (View bobbing factor) float 1.0 - -# Multiplier for fall bobbing. -# For example: 0 for no view bobbing; 1.0 for normal; 2.0 for double. -fall_bobbing_amount (Fall bobbing factor) float 0.03 - -# 3D support. -# Currently supported: -# - none: no 3d output. -# - anaglyph: cyan/magenta color 3d. -# - interlaced: odd/even line based polarisation screen support. -# - topbottom: split screen top/bottom. -# - sidebyside: split screen side by side. -# - crossview: Cross-eyed 3d -# - pageflip: quadbuffer based 3d. -# Note that the interlaced mode requires shaders to be enabled. -3d_mode (3D mode) enum none none,anaglyph,interlaced,topbottom,sidebyside,crossview,pageflip - -# Strength of 3D mode parallax. -3d_paralax_strength (3D mode parallax strength) float 0.025 +language (Language) enum ,be,bg,ca,cs,da,de,el,en,eo,es,et,eu,fi,fr,gd,gl,hu,id,it,ja,jbo,kk,ko,lt,lv,ms,nb,nl,nn,pl,pt,pt_BR,ro,ru,sk,sl,sr_Cyrl,sr_Latn,sv,sw,tr,uk,vi,zh_CN,zh_TW -# In-game chat console height, between 0.1 (10%) and 1.0 (100%). -console_height (Console height) float 0.6 0.1 1.0 +[**GUIs] -# In-game chat console background color (R,G,B). -console_color (Console color) string (0,0,0) +# Scale GUI by a user specified value. +# Use a nearest-neighbor-anti-alias filter to scale the GUI. +# This will smooth over some of the rough edges, and blend +# pixels when scaling down, at the cost of blurring some +# edge pixels when images are scaled by non-integer sizes. +gui_scaling (GUI scaling) float 1.0 0.5 20 -# In-game chat console background alpha (opaqueness, between 0 and 255). -console_alpha (Console alpha) int 200 0 255 +# Enables animation of inventory items. +inventory_items_animations (Inventory items animations) bool false # Formspec full-screen background opacity (between 0 and 255). formspec_fullscreen_bg_opacity (Formspec Full-Screen Background Opacity) int 140 0 255 @@ -711,112 +481,6 @@ formspec_fullscreen_bg_opacity (Formspec Full-Screen Background Opacity) int 140 # Formspec full-screen background color (R,G,B). formspec_fullscreen_bg_color (Formspec Full-Screen Background Color) string (0,0,0) -# Formspec default background opacity (between 0 and 255). -formspec_default_bg_opacity (Formspec Default Background Opacity) int 140 0 255 - -# Formspec default background color (R,G,B). -formspec_default_bg_color (Formspec Default Background Color) string (0,0,0) - -# Selection box border color (R,G,B). -selectionbox_color (Selection box color) string (0,0,0) - -# Width of the selection box lines around nodes. -selectionbox_width (Selection box width) int 2 1 5 - -# Crosshair color (R,G,B). -# Also controls the object crosshair color -crosshair_color (Crosshair color) string (255,255,255) - -# Crosshair alpha (opaqueness, between 0 and 255). -# This also applies to the object crosshair. -crosshair_alpha (Crosshair alpha) int 255 0 255 - -# Maximum number of recent chat messages to show -recent_chat_messages (Recent Chat Messages) int 6 2 20 - -# Whether node texture animations should be desynchronized per mapblock. -desynchronize_mapblock_texture_animation (Desynchronize block animation) bool true - -# Maximum proportion of current window to be used for hotbar. -# Useful if there's something to be displayed right or left of hotbar. -hud_hotbar_max_width (Maximum hotbar width) float 1.0 - -# Modifies the size of the HUD elements. -hud_scaling (HUD scale factor) float 1.0 - -# Enables caching of facedir rotated meshes. -enable_mesh_cache (Mesh cache) bool false - -# Delay between mesh updates on the client in ms. Increasing this will slow -# down the rate of mesh updates, thus reducing jitter on slower clients. -mesh_generation_interval (Mapblock mesh generation delay) int 0 0 50 - -# Size of the MapBlock cache of the mesh generator. Increasing this will -# increase the cache hit %, reducing the data being copied from the main -# thread, thus reducing jitter. -meshgen_block_cache_size (Mapblock mesh generator's MapBlock cache size in MB) int 20 0 1000 - -# Enables minimap. -enable_minimap (Minimap) bool true - -# Shape of the minimap. Enabled = round, disabled = square. -minimap_shape_round (Round minimap) bool true - -# True = 256 -# False = 128 -# Usable to make minimap smoother on slower machines. -minimap_double_scan_height (Minimap scan height) bool true - -# Make fog and sky colors depend on daytime (dawn/sunset) and view direction. -directional_colored_fog (Colored fog) bool true - -# The strength (darkness) of node ambient-occlusion shading. -# Lower is darker, Higher is lighter. The valid range of values for this -# setting is 0.25 to 4.0 inclusive. If the value is out of range it will be -# set to the nearest valid value. -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 - -# Makes all liquids opaque -opaque_water (Opaque liquids) bool false - -# Textures on a node may be aligned either to the node or to the world. -# The former mode suits better things like machines, furniture, etc., while -# the latter makes stairs and microblocks fit surroundings better. -# However, as this possibility is new, thus may not be used by older servers, -# this option allows enforcing it for certain node types. Note though that -# that is considered EXPERIMENTAL and may not work properly. -world_aligned_mode (World-aligned textures mode) enum enable disable,enable,force_solid,force_nodebox - -# World-aligned textures may be scaled to span several nodes. However, -# the server may not send the scale you want, especially if you use -# a specially-designed texture pack; with this option, the client tries -# to determine the scale automatically basing on the texture size. -# See also texture_min_size. -# Warning: This option is EXPERIMENTAL! -autoscale_mode (Autoscaling mode) enum disable disable,enable,force - -# Show entity selection boxes -# A restart is required after changing this. -show_entity_selectionbox (Show entity selection boxes) bool false - -[*Menus] - -# Use a cloud animation for the main menu background. -menu_clouds (Clouds in menu) bool true - -# Scale GUI by a user specified value. -# Use a nearest-neighbor-anti-alias filter to scale the GUI. -# This will smooth over some of the rough edges, and blend -# pixels when scaling down, at the cost of blurring some -# edge pixels when images are scaled by non-integer sizes. -gui_scaling (GUI scaling) float 1.0 0.001 - # When gui_scaling_filter is true, all GUI images need to be # filtered in software, but some images are generated directly # to hardware (e.g. render-to-texture for nodes in inventory). @@ -829,170 +493,105 @@ gui_scaling_filter (GUI scaling filter) bool false gui_scaling_filter_txr2img (GUI scaling filter txr2img) bool true # Delay showing tooltips, stated in milliseconds. -tooltip_show_delay (Tooltip delay) int 400 +tooltip_show_delay (Tooltip delay) int 400 0 18446744073709551615 # Append item name to tooltip. tooltip_append_itemname (Append item name) bool false -font_bold (Font bold by default) bool false - -font_italic (Font italic by default) bool false +# Use a cloud animation for the main menu background. +menu_clouds (Clouds in menu) bool true -# Shadow offset (in pixels) of the default font. If 0, then shadow will not be drawn. -font_shadow (Font shadow) int 1 +[**HUD] -# Opaqueness (alpha) of the shadow behind the default font, between 0 and 255. -font_shadow_alpha (Font shadow alpha) int 127 0 255 +# Modifies the size of the HUD elements. +hud_scaling (HUD scaling) float 1.0 0.5 20 -# Font size of the default font where 1 unit = 1 pixel at 96 DPI -font_size (Font size) int 16 1 +# Whether name tag backgrounds should be shown by default. +# Mods may still set a background. +show_nametag_backgrounds (Show name tag backgrounds by default) bool true -# For pixel-style fonts that do not scale well, this ensures that font sizes used -# with this font will always be divisible by this value, in pixels. For instance, -# a pixel font 16 pixels tall should have this set to 16, so it will only ever be -# sized 16, 32, 48, etc., so a mod requesting a size of 25 will get 32. -font_size_divisible_by (Font size divisible by) int 1 1 +[**Chat] -# Path to the default font. Must be a TrueType font. -# The fallback font will be used if the font cannot be loaded. -font_path (Regular font path) filepath fonts/Arimo-Regular.ttf +# Maximum number of recent chat messages to show +recent_chat_messages (Recent Chat Messages) int 6 2 20 -font_path_bold (Bold font path) filepath fonts/Arimo-Bold.ttf -font_path_italic (Italic font path) filepath fonts/Arimo-Italic.ttf -font_path_bold_italic (Bold and italic font path) filepath fonts/Arimo-BoldItalic.ttf +# In-game chat console height, between 0.1 (10%) and 1.0 (100%). +console_height (Console height) float 0.6 0.1 1.0 -# Font size of the monospace font where 1 unit = 1 pixel at 96 DPI -mono_font_size (Monospace font size) int 16 1 +# In-game chat console background color (R,G,B). +console_color (Console color) string (0,0,0) -# For pixel-style fonts that do not scale well, this ensures that font sizes used -# with this font will always be divisible by this value, in pixels. For instance, -# a pixel font 16 pixels tall should have this set to 16, so it will only ever be -# sized 16, 32, 48, etc., so a mod requesting a size of 25 will get 32. -mono_font_size_divisible_by (Monospace font size divisible by) int 1 1 +# In-game chat console background alpha (opaqueness, between 0 and 255). +console_alpha (Console alpha) int 200 0 255 -# Path to the monospace font. Must be a TrueType font. -# This font is used for e.g. the console and profiler screen. -mono_font_path (Monospace font path) filepath fonts/Cousine-Regular.ttf +# Maximum proportion of current window to be used for hotbar. +# Useful if there's something to be displayed right or left of hotbar. +hud_hotbar_max_width (Maximum hotbar width) float 1.0 0.001 1.0 -mono_font_path_bold (Bold monospace font path) filepath fonts/Cousine-Bold.ttf -mono_font_path_italic (Italic monospace font path) filepath fonts/Cousine-Italic.ttf -mono_font_path_bold_italic (Bold and italic monospace font path) filepath fonts/Cousine-BoldItalic.ttf +# Clickable weblinks (middle-click or Ctrl+left-click) enabled in chat console output. +clickable_chat_weblinks (Chat weblinks) bool true -# Path of the fallback font. Must be a TrueType font. -# This font will be used for certain languages or if the default font is unavailable. -fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf +# Optional override for chat weblink color. +chat_weblink_color (Weblink color) string # Font size of the recent chat text and chat prompt in point (pt). # Value 0 will use the default font size. -chat_font_size (Chat font size) int 0 - -# Path to save screenshots at. Can be an absolute or relative path. -# The folder will be created if it doesn't already exist. -screenshot_path (Screenshot folder) path screenshots - -# Format of screenshots. -screenshot_format (Screenshot format) enum png png,jpg - -# Screenshot quality. Only used for JPEG format. -# 1 means worst quality; 100 means best quality. -# Use 0 for default quality. -screenshot_quality (Screenshot quality) int 0 0 100 - -[*Advanced] +chat_font_size (Chat font size) int 0 0 72 -# Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens. -screen_dpi (DPI) int 72 1 -# Adjust the detected display density, used for scaling UI elements. -display_density_factor (Display Density Scaling Factor) float 1 +[**Content Repository] -# Windows systems only: Start Minetest with the command line window in the background. -# Contains the same information as the file debug.txt (default name). -enable_console (Enable console window) bool false - -[Sound] - -# Enables the sound system. -# If disabled, this completely disables all sounds everywhere and the in-game -# sound controls will be non-functional. -# Changing this setting requires a restart. -enable_sound (Sound) bool true - -# Volume of all sounds. -# Requires the sound system to be enabled. -sound_volume (Volume) float 0.7 0.0 1.0 - -# Whether to mute sounds. You can unmute sounds at any time, unless the -# sound system is disabled (enable_sound=false). -# In-game, you can toggle the mute state with the mute key or by using the -# pause menu. -mute_sound (Mute sound) bool false - -[Client] +# The URL for the content repository +contentdb_url (ContentDB URL) string https://content.minetest.net -# Clickable weblinks (middle-click or Ctrl+left-click) enabled in chat console output. -clickable_chat_weblinks (Chat weblinks) bool false +# Comma-separated list of flags to hide in the content repository. +# "nonfree" can be used to hide packages which do not qualify as 'free software', +# as defined by the Free Software Foundation. +# You can also specify content ratings. +# These flags are independent from Minetest versions, +# so see a full list at https://content.minetest.net/help/content_flags/ +contentdb_flag_blacklist (ContentDB Flag Blacklist) string nonfree, desktop_default -# Optional override for chat weblink color. -chat_weblink_color (Weblink color) string +# Maximum number of concurrent downloads. Downloads exceeding this limit will be queued. +# This should be lower than curl_parallel_limit. +contentdb_max_concurrent_downloads (ContentDB Max Concurrent Downloads) int 3 1 -[*Network] -# Address to connect to. -# Leave this blank to start a local server. -# Note that the address field in the main menu overrides this setting. -address (Server address) string +[Client and Server] -# Port to connect to (UDP). -# Note that the port field in the main menu overrides this setting. -remote_port (Remote port) int 30000 1 65535 - -# Prometheus listener address. -# If Minetest is compiled with ENABLE_PROMETHEUS option enabled, -# enable metrics listener for Prometheus on that address. -# Metrics can be fetched on http://127.0.0.1:30000/metrics -prometheus_listener_address (Prometheus listener address) string 127.0.0.1:30000 +[*Client] # Save the map received by the client on disk. enable_local_map_saving (Saving map received from server) bool false -# Enable usage of remote media server (if provided by server). -# Remote servers offer a significantly faster way to download media (e.g. textures) -# when connecting to the server. -enable_remote_media_server (Connect to external media server) bool true - -# Enable Lua modding support on client. -# This support is experimental and API can change. -enable_client_modding (Client modding) bool false - # URL to the server list displayed in the Multiplayer Tab. serverlist_url (Serverlist URL) string servers.minetest.net -# File in client/serverlist/ that contains your favorite servers displayed in the -# Multiplayer Tab. -serverlist_file (Serverlist file) string favoriteservers.json - -# Maximum size of the out chat queue. -# 0 to disable queueing and -1 to make the queue size unlimited. -max_out_chat_queue_size (Maximum size of the out chat queue) int 20 +# If enabled, account registration is separate from login in the UI. +# If disabled, new accounts will be registered automatically when logging in. +enable_split_login_register (Enable split login/register) bool true -# Enable register confirmation when connecting to server. -# If disabled, new account will be registered automatically. -enable_register_confirmation (Enable register confirmation) bool true +# URL to JSON file which provides information about the newest Minetest release +update_information_url (Update information URL) string https://www.minetest.net/release_info.json -[*Advanced] +# Unix timestamp (integer) of when the client last checked for an update +# Set this value to "disabled" to never check for updates. +update_last_checked (Last update check) string -# Timeout for client to remove unused map data from memory. -client_unload_unused_data_timeout (Mapblock unload timeout) int 600 +# Version number which was last seen during an update check. +# +# Representation: MMMIIIPPP, where M=Major, I=Minor, P=Patch +# Ex: 5.5.0 is 005005000 +update_last_known (Last known version update) int 0 -# Maximum number of mapblocks for client to be kept in memory. -# Set to -1 for unlimited amount. -client_mapblock_limit (Mapblock limit) int 7500 +[*Server] -# Whether to show the client debug info (has the same effect as hitting F5). -show_debug (Show debug info) bool false +# Name of the player. +# When running a server, clients connecting with this name are admins. +# When starting from the main menu, this is overridden. +name (Admin name) string -[Server / Singleplayer] +[**Serverlist and MOTD] # Name of the server, to be displayed when players join and in the serverlist. server_name (Server name) string Minetest server @@ -1012,15 +611,20 @@ server_announce (Announce server) bool false # Announce to this serverlist. serverlist_url (Serverlist URL) string servers.minetest.net -# Remove color codes from incoming chat messages -# Use this to stop players from being able to use color in their messages -strip_color_codes (Strip color codes) bool false +# Message of the day displayed to players connecting. +motd (Message of the day) string -[*Network] +# Maximum number of players that can be connected simultaneously. +max_users (Maximum users) int 15 0 65535 + +# If this is set, players will always (re)spawn at the given position. +static_spawnpoint (Static spawnpoint) string + +[**Networking] # Network port to listen (UDP). # This value will be overridden when starting from the main menu. -port (Server port) int 30000 +port (Server port) int 30000 1 65535 # The network interface that the server listens on. bind_address (Bind address) string @@ -1041,65 +645,14 @@ remote_media (Remote media) string # Needs enable_ipv6 to be enabled. ipv6_server (IPv6 server) bool false -[**Advanced] - -# Maximum number of blocks that are simultaneously sent per client. -# The maximum total count is calculated dynamically: -# max_total = ceil((#clients + max_users) * per_client / 4) -max_simultaneous_block_sends_per_client (Maximum simultaneous block sends per client) 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 (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 -# client number. -max_packets_per_iteration (Max. packets per iteration) int 1024 - -# Compression level to use when sending mapblocks to the client. -# -1 - use default compression level -# 0 - least compression, fastest -# 9 - best compression, slowest -map_compression_level_net (Map Compression Level for Network Transfer) int -1 -1 9 - -[*Game] - -# Default game when creating a new world. -# This will be overridden when creating a world from the main menu. -default_game (Default game) string minetest - -# Message of the day displayed to players connecting. -motd (Message of the day) string - -# Maximum number of players that can be connected simultaneously. -max_users (Maximum users) int 15 - -# World directory (everything in the world is stored here). -# Not needed if starting from the main menu. -map-dir (Map directory) path - -# Time in seconds for item entity (dropped items) to live. -# Setting it to -1 disables the feature. -item_entity_ttl (Item entity TTL) int 900 - -# Specifies the default stack size of nodes, items and tools. -# Note that mods or games may explicitly set a stack for certain (or all) items. -default_stack_max (Default stack size) int 99 - -# Enable players getting damage and dying. -enable_damage (Damage) bool false - -# Enable creative mode for all players -creative_mode (Creative) bool false - -# A chosen map seed for a new map, leave empty for random. -# Will be overridden when creating a new world in the main menu. -fixed_map_seed (Fixed map seed) string +[*Server Security] # New users need to input this password. default_password (Default password) string +# If enabled, players cannot join without a password or change theirs to an empty password. +disallow_empty_password (Disallow empty passwords) bool false + # The privileges that new users automatically get. # See /privs in game for a full list on your server and mod configuration. default_privs (Default privileges) string interact, shout @@ -1107,25 +660,6 @@ default_privs (Default privileges) string interact, shout # Privileges that players with basic_privs can grant basic_privs (Basic privileges) string interact, shout -# Whether players are shown to clients without any range limit. -# Deprecated, use the setting player_transfer_distance instead. -unlimited_player_transfer_distance (Unlimited player transfer distance) bool true - -# Defines the maximal player transfer distance in blocks (0 = unlimited). -player_transfer_distance (Player transfer distance) int 0 - -# Whether to allow players to damage and kill each other. -enable_pvp (Player versus player) bool true - -# Enable mod channels support. -enable_mod_channels (Mod channels) bool false - -# If this is set, players will always (re)spawn at the given position. -static_spawnpoint (Static spawnpoint) string - -# If enabled, players cannot join without a password or change theirs to an empty password. -disallow_empty_password (Disallow empty passwords) bool false - # If enabled, disable cheat prevention in multiplayer. disable_anticheat (Disable anticheat) bool false @@ -1133,317 +667,108 @@ disable_anticheat (Disable anticheat) bool false # This option is only read when server starts. enable_rollback_recording (Rollback recording) bool false -# Format of player chat messages. The following strings are valid placeholders: -# @name, @message, @timestamp (optional) -chat_message_format (Chat message format) string <@name> @message - -# If the execution of a chat command takes longer than this specified time in -# seconds, add the time information to the chat command message -chatcommand_msg_time_threshold (Chat command time message threshold) float 0.1 +[**Client-side Modding] -# A message to be displayed to all clients when the server shuts down. -kick_msg_shutdown (Shutdown message) string Server shutting down. +# Restricts the access of certain client-side functions on servers. +# Combine the byteflags below to restrict client-side features, or set to 0 +# for no restrictions: +# LOAD_CLIENT_MODS: 1 (disable loading client-provided mods) +# CHAT_MESSAGES: 2 (disable send_chat_message call client-side) +# READ_ITEMDEFS: 4 (disable get_item_def call client-side) +# READ_NODEDEFS: 8 (disable get_node_def call client-side) +# LOOKUP_NODES_LIMIT: 16 (limits get_node call client-side to +# csm_restriction_noderange) +# READ_PLAYERINFO: 32 (disable get_player_names call client-side) +csm_restriction_flags (Client side modding restrictions) int 62 0 63 -# A message to be displayed to all clients when the server crashes. -kick_msg_crash (Crash message) string This server has experienced an internal error. You will now be disconnected. +# If the CSM restriction for node range is enabled, get_node calls are limited +# to this distance from the player to the node. +csm_restriction_noderange (Client side node lookup range restriction) int 0 0 4294967295 -# Whether to ask clients to reconnect after a (Lua) crash. -# Set this to true if your server is set up to restart automatically. -ask_reconnect_on_crash (Ask to reconnect after crash) bool false +[**Chat] -# From how far clients know about objects, stated in mapblocks (16 nodes). -# -# Setting this larger than active_block_range will also cause the server -# to maintain active objects up to this distance in the direction the -# player is looking. (This can avoid mobs suddenly disappearing from view) -active_object_send_range_blocks (Active object send range) int 8 +# Remove color codes from incoming chat messages +# Use this to stop players from being able to use color in their messages +strip_color_codes (Strip color codes) bool false -# The radius of the volume of blocks around every player that is subject to the -# active block stuff, stated in mapblocks (16 nodes). -# In active blocks objects are loaded and ABMs run. -# This is also the minimum range in which active objects (mobs) are maintained. -# This should be configured together with active_object_send_range_blocks. -active_block_range (Active block range) int 4 +# Set the maximum length of a chat message (in characters) sent by clients. +chat_message_max_size (Chat message max length) int 500 10 65535 -# From how far blocks are sent to clients, stated in mapblocks (16 nodes). -max_block_send_distance (Max block send distance) int 12 +# Amount of messages a player may send per 10 seconds. +chat_message_limit_per_10sec (Chat message count limit) float 10.0 1.0 -# Maximum number of forceloaded mapblocks. -max_forceloaded_blocks (Maximum forceloaded blocks) int 16 +# Kick players who sent more than X messages per 10 seconds. +chat_message_limit_trigger_kick (Chat message kick threshold) int 50 1 65535 -# Interval of sending time of day to clients. -time_send_interval (Time send interval) int 5 +[*Server Gameplay] # Controls length of day/night cycle. # Examples: # 72 = 20min, 360 = 4min, 1 = 24hour, 0 = day/night/whatever stays unchanged. -time_speed (Time speed) int 72 +time_speed (Time speed) int 72 0 # Time of day when a new world is started, in millihours (0-23999). world_start_time (World start time) int 6125 0 23999 -# 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 (Chat message max length) int 500 - -# Amount of messages a player may send per 10 seconds. -chat_message_limit_per_10sec (Chat message count limit) float 10.0 +# Time in seconds for item entity (dropped items) to live. +# Setting it to -1 disables the feature. +item_entity_ttl (Item entity TTL) int 900 -1 -# Kick players who sent more than X messages per 10 seconds. -chat_message_limit_trigger_kick (Chat message kick threshold) int 50 +# Specifies the default stack size of nodes, items and tools. +# Note that mods or games may explicitly set a stack for certain (or all) items. +default_stack_max (Default stack size) int 99 1 65535 [**Physics] # Horizontal and vertical acceleration on ground or when climbing, # in nodes per second per second. -movement_acceleration_default (Default acceleration) float 3 +movement_acceleration_default (Default acceleration) float 3.0 0.0 # Horizontal acceleration in air when jumping or falling, # in nodes per second per second. -movement_acceleration_air (Acceleration in air) float 2 +movement_acceleration_air (Acceleration in air) float 2.0 0.0 # Horizontal and vertical acceleration in fast mode, # in nodes per second per second. -movement_acceleration_fast (Fast mode acceleration) float 10 +movement_acceleration_fast (Fast mode acceleration) float 10.0 0.0 # Walking and flying speed, in nodes per second. -movement_speed_walk (Walking speed) float 4 +movement_speed_walk (Walking speed) float 4.0 0.0 # Sneaking speed, in nodes per second. -movement_speed_crouch (Sneaking speed) float 1.35 +movement_speed_crouch (Sneaking speed) float 1.35 0.0 # Walking, flying and climbing speed in fast mode, in nodes per second. -movement_speed_fast (Fast mode speed) float 20 +movement_speed_fast (Fast mode speed) float 20.0 0.0 # Vertical climbing speed, in nodes per second. -movement_speed_climb (Climbing speed) float 3 +movement_speed_climb (Climbing speed) float 3.0 0.0 # Initial vertical speed when jumping, in nodes per second. -movement_speed_jump (Jumping speed) float 6.5 +movement_speed_jump (Jumping speed) float 6.5 0.0 +# How much you are slowed down when moving inside a liquid. # Decrease this to increase liquid resistance to movement. -movement_liquid_fluidity (Liquid fluidity) float 1 +movement_liquid_fluidity (Liquid fluidity) float 1.0 0.001 # Maximum liquid resistance. Controls deceleration when entering liquid at # high speed. movement_liquid_fluidity_smooth (Liquid fluidity smoothing) float 0.5 -# Controls sinking speed in liquid. -movement_liquid_sink (Liquid sinking) float 10 +# Controls sinking speed in liquid when idling. Negative values will cause +# you to rise instead. +movement_liquid_sink (Liquid sinking) float 10.0 # Acceleration of gravity, in nodes per second per second. movement_gravity (Gravity) float 9.81 -[**Advanced] - -# Handling for deprecated Lua API calls: -# - none: Do not log deprecated calls -# - log: mimic and log backtrace of deprecated call (default). -# - error: abort on usage of deprecated call (suggested for mod developers). -deprecated_lua_api_handling (Deprecated Lua API handling) enum log none,log,error - -# 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). -max_clearobjects_extra_loaded_blocks (Max. clearobjects extra blocks) int 4096 - -# How much the server will wait before unloading unused mapblocks. -# Higher value is smoother, but will use more RAM. -server_unload_unused_data_timeout (Unload unused server data) int 29 - -# Maximum number of statically stored objects in a block. -max_objects_per_block (Maximum objects per block) int 64 - -# See https://www.sqlite.org/pragma.html#pragma_synchronous -sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2 - -# Compression level to use when saving mapblocks to disk. -# -1 - use default compression level -# 0 - least compression, fastest -# 9 - best compression, slowest -map_compression_level_disk (Map Compression Level for Disk Storage) int -1 -1 9 - -# Length of a server tick and the interval at which objects are generally updated over -# network. -dedicated_server_step (Dedicated server step) float 0.09 - -# Length of time between active block management cycles -active_block_mgmt_interval (Active block management interval) float 2.0 - -# Length of time between Active Block Modifier (ABM) execution cycles -abm_interval (ABM interval) float 1.0 - -# The time budget allowed for ABMs to execute on each step -# (as a fraction of the ABM Interval) -abm_time_budget (ABM time budget) float 0.2 0.1 0.9 - -# Length of time between NodeTimer execution cycles -nodetimer_interval (NodeTimer interval) float 0.2 - -# If enabled, invalid world data won't cause the server to shut down. -# Only enable this if you know what you are doing. -ignore_world_load_errors (Ignore world errors) bool false - -# Max liquids processed per step. -liquid_loop_max (Liquid loop max) int 100000 - -# The time (in seconds) that the liquids queue may grow beyond processing -# capacity until an attempt is made to decrease its size by dumping old queue -# items. A value of 0 disables the functionality. -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 - -# If enabled the server will perform map block occlusion culling based on -# on the eye position of the player. This can reduce the number of blocks -# sent to the client 50-80%. The client will not longer receive most invisible -# so that the utility of noclip mode is reduced. -server_side_occlusion_culling (Server side occlusion culling) bool true - -# Restricts the access of certain client-side functions on servers. -# Combine the byteflags below to restrict client-side features, or set to 0 -# for no restrictions: -# LOAD_CLIENT_MODS: 1 (disable loading client-provided mods) -# CHAT_MESSAGES: 2 (disable send_chat_message call client-side) -# READ_ITEMDEFS: 4 (disable get_item_def call client-side) -# READ_NODEDEFS: 8 (disable get_node_def call client-side) -# LOOKUP_NODES_LIMIT: 16 (limits get_node call client-side to -# csm_restriction_noderange) -# READ_PLAYERINFO: 32 (disable get_player_names call client-side) -csm_restriction_flags (Client side modding restrictions) int 62 - -# If the CSM restriction for node range is enabled, get_node calls are limited -# to this distance from the player to the node. -csm_restriction_noderange (Client side node lookup range restriction) int 0 - -[*Security] - -# Prevent mods from doing insecure things like running shell commands. -secure.enable_security (Enable mod security) bool true - -# Comma-separated list of trusted mods that are allowed to access insecure -# functions even when mod security is on (via request_insecure_environment()). -secure.trusted_mods (Trusted mods) string - -# 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 chat commands on registration. -instrument.chatcommand (Chat commands) 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. -# When running a server, clients connecting with this name are admins. -# When starting from the main menu, this is overridden. -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,bg,ca,cs,da,de,el,en,eo,es,et,eu,fi,fr,gd,gl,hu,id,it,ja,jbo,kk,ko,lt,lv,ms,nb,nl,nn,pl,pt,pt_BR,ro,ru,sk,sl,sr_Cyrl,sr_Latn,sv,sw,tr,uk,vi,zh_CN,zh_TW - -# Level of logging to be written to debug.txt: -# - <nothing> (no logging) -# - none (messages with no level) -# - error -# - warning -# - action -# - info -# - verbose -debug_log_level (Debug log level) enum action ,none,error,warning,action,info,verbose - -# If the file size of debug.txt exceeds the number of megabytes specified in -# this setting when it is opened, the file is moved to debug.txt.1, -# deleting an older debug.txt.1 if it exists. -# debug.txt is only moved if this setting is positive. -debug_log_size_max (Debug log file size threshold) int 50 - -# Minimal level of logging to be written to chat. -chat_log_level (Chat log level) enum error ,none,error,warning,action,info,verbose - -# Enable IPv6 support (for both client and server). -# Required for IPv6 connections to work at all. -enable_ipv6 (IPv6) bool true - -[*Advanced] - -# Maximum time an interactive request (e.g. server list fetch) may take, stated in milliseconds. -curl_timeout (cURL interactive timeout) int 20000 - -# Limits number of parallel HTTP requests. Affects: -# - Media fetch if server uses remote_media setting. -# - Serverlist download and server announcement. -# - Downloads performed by main menu (e.g. mod manager). -# Only has an effect if compiled with cURL. -curl_parallel_limit (cURL parallel limit) int 8 - -# Maximum time a file download (e.g. a mod download) may take, stated in milliseconds. -curl_file_download_timeout (cURL file download timeout) int 300000 - -# Replaces the default main menu with a custom one. -main_menu_script (Main menu script) string - -# 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 [Mapgen] +# A chosen map seed for a new map, leave empty for random. +# Will be overridden when creating a new world in the main menu. +fixed_map_seed (Fixed map seed) string + # Name of map generator to be used when creating a new world. # Creating a world in the main menu will override this. # Current mapgens in a highly unstable state: @@ -1451,10 +776,10 @@ profiler_print_interval (Engine profiling data print interval) int 0 mg_name (Mapgen name) enum v7 v7,valleys,carpathian,v5,flat,fractal,singlenode,v6 # Water surface level of the world. -water_level (Water level) int 1 +water_level (Water level) int 1 -31000 31000 # From how far blocks are generated for clients, stated in mapblocks (16 nodes). -max_block_generate_distance (Max block generate distance) int 10 +max_block_generate_distance (Max block generate distance) int 10 1 32767 # Limit of map generation, in nodes, in all 6 directions from (0, 0, 0). # Only mapchunks completely within the mapgen limit are generated. @@ -1466,7 +791,7 @@ mapgen_limit (Map generation limit) int 31007 0 31007 # and jungle grass, in all other mapgens this flag controls all decorations. mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes,ores caves,dungeons,light,decorations,biomes,ores,nocaves,nodungeons,nolight,nodecorations,nobiomes,noores -[*Biome API temperature and humidity noise parameters] +[*Biome API noise parameters] # Temperature variation for biomes. mg_biome_np_heat (Heat noise) noise_params_2d 50, 50, (1000, 1000, 1000), 5349, 3, 0.5, 2.0, eased @@ -1491,7 +816,7 @@ mgv5_spflags (Mapgen V5 specific flags) flags caverns caverns,nocaverns mgv5_cave_width (Cave width) float 0.09 # Y of upper limit of large caves. -mgv5_large_cave_depth (Large cave depth) int -256 +mgv5_large_cave_depth (Large cave depth) int -256 -31000 31000 # Minimum limit of random number of small caves per mapchunk. mgv5_small_cave_num_min (Small cave minimum number) int 0 0 256 @@ -1509,19 +834,19 @@ mgv5_large_cave_num_max (Large cave maximum number) int 2 0 64 mgv5_large_cave_flooded (Large cave proportion flooded) float 0.5 0.0 1.0 # Y-level of cavern upper limit. -mgv5_cavern_limit (Cavern limit) int -256 +mgv5_cavern_limit (Cavern limit) int -256 -31000 31000 # Y-distance over which caverns expand to full size. -mgv5_cavern_taper (Cavern taper) int 256 +mgv5_cavern_taper (Cavern taper) int 256 0 32767 # Defines full size of caverns, smaller values create larger caverns. mgv5_cavern_threshold (Cavern threshold) float 0.7 # Lower Y limit of dungeons. -mgv5_dungeon_ymin (Dungeon minimum Y) int -31000 +mgv5_dungeon_ymin (Dungeon minimum Y) int -31000 -31000 31000 # Upper Y limit of dungeons. -mgv5_dungeon_ymax (Dungeon maximum Y) int 31000 +mgv5_dungeon_ymax (Dungeon maximum Y) int 31000 -31000 31000 [**Noises] @@ -1566,10 +891,10 @@ mgv6_freq_desert (Desert noise threshold) float 0.45 mgv6_freq_beach (Beach noise threshold) float 0.15 # Lower Y limit of dungeons. -mgv6_dungeon_ymin (Dungeon minimum Y) int -31000 +mgv6_dungeon_ymin (Dungeon minimum Y) int -31000 -31000 31000 # Upper Y limit of dungeons. -mgv6_dungeon_ymax (Dungeon maximum Y) int 31000 +mgv6_dungeon_ymax (Dungeon maximum Y) int 31000 -31000 31000 [**Noises] @@ -1615,19 +940,19 @@ mgv6_np_apple_trees (Apple trees noise) noise_params_2d 0, 1, (100, 100, 100), 3 mgv7_spflags (Mapgen V7 specific flags) flags mountains,ridges,nofloatlands,caverns mountains,ridges,floatlands,caverns,nomountains,noridges,nofloatlands,nocaverns # Y of mountain density gradient zero level. Used to shift mountains vertically. -mgv7_mount_zero_level (Mountain zero level) int 0 +mgv7_mount_zero_level (Mountain zero level) int 0 -31000 31000 # Lower Y limit of floatlands. -mgv7_floatland_ymin (Floatland minimum Y) int 1024 +mgv7_floatland_ymin (Floatland minimum Y) int 1024 -31000 31000 # Upper Y limit of floatlands. -mgv7_floatland_ymax (Floatland maximum Y) int 4096 +mgv7_floatland_ymax (Floatland maximum Y) int 4096 -31000 31000 # Y-distance over which floatlands taper from full density to nothing. # Tapering starts at this distance from the Y limit. # For a solid floatland layer, this controls the height of hills/mountains. # Must be less than or equal to half the distance between the Y limits. -mgv7_floatland_taper (Floatland tapering distance) int 256 +mgv7_floatland_taper (Floatland tapering distance) int 256 0 32767 # Exponent of the floatland tapering. Alters the tapering behaviour. # Value = 1.0 creates a uniform, linear tapering. @@ -1654,7 +979,7 @@ mgv7_floatland_density (Floatland density) float -0.6 # required value depending on 'mgv7_np_floatland'), to avoid # server-intensive extreme water flow and to avoid vast flooding of the # world surface below. -mgv7_floatland_ywater (Floatland water level) int -31000 +mgv7_floatland_ywater (Floatland water level) int -31000 -31000 31000 # Controls width of tunnels, a smaller value creates wider tunnels. # Value >= 10.0 completely disables generation of tunnels and avoids the @@ -1662,7 +987,7 @@ mgv7_floatland_ywater (Floatland water level) int -31000 mgv7_cave_width (Cave width) float 0.09 # Y of upper limit of large caves. -mgv7_large_cave_depth (Large cave depth) int -33 +mgv7_large_cave_depth (Large cave depth) int -33 -31000 31000 # Minimum limit of random number of small caves per mapchunk. mgv7_small_cave_num_min (Small cave minimum number) int 0 0 256 @@ -1680,19 +1005,19 @@ mgv7_large_cave_num_max (Large cave maximum number) int 2 0 64 mgv7_large_cave_flooded (Large cave proportion flooded) float 0.5 0.0 1.0 # Y-level of cavern upper limit. -mgv7_cavern_limit (Cavern limit) int -256 +mgv7_cavern_limit (Cavern limit) int -256 -31000 31000 # Y-distance over which caverns expand to full size. -mgv7_cavern_taper (Cavern taper) int 256 +mgv7_cavern_taper (Cavern taper) int 256 0 32767 # Defines full size of caverns, smaller values create larger caverns. mgv7_cavern_threshold (Cavern threshold) float 0.7 # Lower Y limit of dungeons. -mgv7_dungeon_ymin (Dungeon minimum Y) int -31000 +mgv7_dungeon_ymin (Dungeon minimum Y) int -31000 -31000 31000 # Upper Y limit of dungeons. -mgv7_dungeon_ymax (Dungeon maximum Y) int 31000 +mgv7_dungeon_ymax (Dungeon maximum Y) int 31000 -31000 31000 [**Noises] @@ -1766,7 +1091,7 @@ mgcarpathian_valley_width (River valley width) float 0.25 mgcarpathian_cave_width (Cave width) float 0.09 # Y of upper limit of large caves. -mgcarpathian_large_cave_depth (Large cave depth) int -33 +mgcarpathian_large_cave_depth (Large cave depth) int -33 -31000 31000 # Minimum limit of random number of small caves per mapchunk. mgcarpathian_small_cave_num_min (Small cave minimum number) int 0 0 256 @@ -1784,19 +1109,19 @@ mgcarpathian_large_cave_num_max (Large cave maximum number) int 2 0 64 mgcarpathian_large_cave_flooded (Large cave proportion flooded) float 0.5 0.0 1.0 # Y-level of cavern upper limit. -mgcarpathian_cavern_limit (Cavern limit) int -256 +mgcarpathian_cavern_limit (Cavern limit) int -256 -31000 31000 # Y-distance over which caverns expand to full size. -mgcarpathian_cavern_taper (Cavern taper) int 256 +mgcarpathian_cavern_taper (Cavern taper) int 256 0 32767 # Defines full size of caverns, smaller values create larger caverns. mgcarpathian_cavern_threshold (Cavern threshold) float 0.7 # Lower Y limit of dungeons. -mgcarpathian_dungeon_ymin (Dungeon minimum Y) int -31000 +mgcarpathian_dungeon_ymin (Dungeon minimum Y) int -31000 -31000 31000 # Upper Y limit of dungeons. -mgcarpathian_dungeon_ymax (Dungeon maximum Y) int 31000 +mgcarpathian_dungeon_ymax (Dungeon maximum Y) int 31000 -31000 31000 [**Noises] @@ -1858,10 +1183,10 @@ mgcarpathian_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 50 mgflat_spflags (Mapgen Flat specific flags) flags nolakes,nohills,nocaverns lakes,hills,caverns,nolakes,nohills,nocaverns # Y of flat ground. -mgflat_ground_level (Ground level) int 8 +mgflat_ground_level (Ground level) int 8 -31000 31000 # Y of upper limit of large caves. -mgflat_large_cave_depth (Large cave depth) int -33 +mgflat_large_cave_depth (Large cave depth) int -33 -31000 31000 # Minimum limit of random number of small caves per mapchunk. mgflat_small_cave_num_min (Small cave minimum number) int 0 0 256 @@ -1900,19 +1225,19 @@ mgflat_hill_threshold (Hill threshold) float 0.45 mgflat_hill_steepness (Hill steepness) float 64.0 # Y-level of cavern upper limit. -mgflat_cavern_limit (Cavern limit) int -256 +mgflat_cavern_limit (Cavern limit) int -256 -31000 31000 # Y-distance over which caverns expand to full size. -mgflat_cavern_taper (Cavern taper) int 256 +mgflat_cavern_taper (Cavern taper) int 256 0 32767 # Defines full size of caverns, smaller values create larger caverns. mgflat_cavern_threshold (Cavern threshold) float 0.7 # Lower Y limit of dungeons. -mgflat_dungeon_ymin (Dungeon minimum Y) int -31000 +mgflat_dungeon_ymin (Dungeon minimum Y) int -31000 -31000 31000 # Upper Y limit of dungeons. -mgflat_dungeon_ymax (Dungeon maximum Y) int 31000 +mgflat_dungeon_ymax (Dungeon maximum Y) int 31000 -31000 31000 [**Noises] @@ -1947,7 +1272,7 @@ mgfractal_spflags (Mapgen Fractal specific flags) flags terrain terrain,noterrai mgfractal_cave_width (Cave width) float 0.09 # Y of upper limit of large caves. -mgfractal_large_cave_depth (Large cave depth) int -33 +mgfractal_large_cave_depth (Large cave depth) int -33 -31000 31000 # Minimum limit of random number of small caves per mapchunk. mgfractal_small_cave_num_min (Small cave minimum number) int 0 0 256 @@ -1965,10 +1290,10 @@ mgfractal_large_cave_num_max (Large cave maximum number) int 2 0 64 mgfractal_large_cave_flooded (Large cave proportion flooded) float 0.5 0.0 1.0 # Lower Y limit of dungeons. -mgfractal_dungeon_ymin (Dungeon minimum Y) int -31000 +mgfractal_dungeon_ymin (Dungeon minimum Y) int -31000 -31000 31000 # Upper Y limit of dungeons. -mgfractal_dungeon_ymax (Dungeon maximum Y) int 31000 +mgfractal_dungeon_ymax (Dungeon maximum Y) int 31000 -31000 31000 # Selects one of 18 fractal types. # 1 = 4D "Roundy" Mandelbrot set. @@ -1995,7 +1320,7 @@ mgfractal_fractal (Fractal type) int 1 1 18 # Increasing this increases the amount of fine detail, but also # increases processing load. # At iterations = 20 this mapgen has a similar load to mapgen V7. -mgfractal_iterations (Iterations) int 11 +mgfractal_iterations (Iterations) int 11 1 65535 # (X,Y,Z) scale of fractal in nodes. # Actual fractal size will be 2 to 3 times larger. @@ -2078,10 +1403,10 @@ mgvalleys_spflags (Mapgen Valleys specific flags) flags altitude_chill,humid_riv # The vertical distance over which heat drops by 20 if 'altitude_chill' is # enabled. Also the vertical distance over which humidity drops by 10 if # 'altitude_dry' is enabled. -mgvalleys_altitude_chill (Altitude chill) int 90 +mgvalleys_altitude_chill (Altitude chill) int 90 0 65535 # Depth below which you'll find large caves. -mgvalleys_large_cave_depth (Large cave depth) int -33 +mgvalleys_large_cave_depth (Large cave depth) int -33 -31000 31000 # Minimum limit of random number of small caves per mapchunk. mgvalleys_small_cave_num_min (Small cave minimum number) int 0 0 256 @@ -2099,19 +1424,19 @@ mgvalleys_large_cave_num_max (Large cave maximum number) int 2 0 64 mgvalleys_large_cave_flooded (Large cave proportion flooded) float 0.5 0.0 1.0 # Depth below which you'll find giant caverns. -mgvalleys_cavern_limit (Cavern upper limit) int -256 +mgvalleys_cavern_limit (Cavern upper limit) int -256 -31000 31000 # Y-distance over which caverns expand to full size. -mgvalleys_cavern_taper (Cavern taper) int 192 +mgvalleys_cavern_taper (Cavern taper) int 192 0 32767 # Defines full size of caverns, smaller values create larger caverns. mgvalleys_cavern_threshold (Cavern threshold) float 0.6 # How deep to make rivers. -mgvalleys_river_depth (River depth) int 4 +mgvalleys_river_depth (River depth) int 4 0 65535 # How wide to make rivers. -mgvalleys_river_size (River size) int 5 +mgvalleys_river_size (River size) int 5 0 65535 # Controls width of tunnels, a smaller value creates wider tunnels. # Value >= 10.0 completely disables generation of tunnels and avoids the @@ -2119,10 +1444,10 @@ mgvalleys_river_size (River size) int 5 mgvalleys_cave_width (Cave width) float 0.09 # Lower Y limit of dungeons. -mgvalleys_dungeon_ymin (Dungeon minimum Y) int -31000 +mgvalleys_dungeon_ymin (Dungeon minimum Y) int -31000 -31000 31000 # Upper Y limit of dungeons. -mgvalleys_dungeon_ymax (Dungeon maximum Y) int 63 +mgvalleys_dungeon_ymax (Dungeon maximum Y) int 63 -31000 31000 [**Noises] @@ -2159,15 +1484,413 @@ mgvalleys_np_inter_valley_slope (Valley slope) noise_params_2d 0.5, 0.5, (128, 1 # 3D noise that determines number of dungeons per mapchunk. mgvalleys_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500), 0, 2, 0.8, 2.0 + +[Advanced] + +[*Developer Options] + +# Enable Lua modding support on client. +# This support is experimental and API can change. +enable_client_modding (Client modding) bool false + +# Replaces the default main menu with a custom one. +main_menu_script (Main menu script) string + +[**Mod Security] + +# Prevent mods from doing insecure things like running shell commands. +secure.enable_security (Enable mod security) bool true + +# Comma-separated list of trusted mods that are allowed to access insecure +# functions even when mod security is on (via request_insecure_environment()). +secure.trusted_mods (Trusted mods) string + +# 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 + +[**Debugging] + +# Level of logging to be written to debug.txt: +# - <nothing> (no logging) +# - none (messages with no level) +# - error +# - warning +# - action +# - info +# - verbose +# - trace +debug_log_level (Debug log level) enum action ,none,error,warning,action,info,verbose,trace + +# If the file size of debug.txt exceeds the number of megabytes specified in +# this setting when it is opened, the file is moved to debug.txt.1, +# deleting an older debug.txt.1 if it exists. +# debug.txt is only moved if this setting is positive. +debug_log_size_max (Debug log file size threshold) int 50 1 + +# Minimal level of logging to be written to chat. +chat_log_level (Chat log level) enum error ,none,error,warning,action,info,verbose,trace + +# Handling for deprecated Lua API calls: +# - none: Do not log deprecated calls +# - log: mimic and log backtrace of deprecated call (default). +# - error: abort on usage of deprecated call (suggested for mod developers). +deprecated_lua_api_handling (Deprecated Lua API handling) enum log none,log,error + +# Enable random user input (only used for testing). +random_input (Random input) bool false + +# Enable mod channels support. +enable_mod_channels (Mod channels) bool false + +[**Mod Profiler] + +# 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 "" + +# 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 chat commands on registration. +instrument.chatcommand (Chat commands) bool true + +# Instrument global callback functions on registration. +# (anything you pass to a minetest.register_*() function) +instrument.global_callback (Global callbacks) bool true + +# 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 + +[**Engine profiler] + +# 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 0 + + [*Advanced] +# Enable IPv6 support (for both client and server). +# Required for IPv6 connections to work at all. +enable_ipv6 (IPv6) bool true + +# If enabled, invalid world data won't cause the server to shut down. +# Only enable this if you know what you are doing. +ignore_world_load_errors (Ignore world errors) bool false + +[**Graphics] + +# Path to shader directory. If no path is defined, default location will be used. +shader_path (Shader path) path + +# The rendering back-end. +# A restart is required after changing this. +# Note: On Android, stick with OGLES1 if unsure! App may fail to start otherwise. +# On other platforms, OpenGL is recommended. +# Shaders are supported by OpenGL (desktop only) and OGLES2 (experimental) +video_driver (Video driver) enum opengl opengl,ogles1,ogles2 + +# Distance in nodes at which transparency depth sorting is enabled +# Use this to limit the performance impact of transparency depth sorting +transparency_sorting_distance (Transparency Sorting Distance) int 16 0 128 + +# Enable vertex buffer objects. +# This should greatly improve graphics performance. +enable_vbo (VBO) bool true + +# Radius of cloud area stated in number of 64 node cloud squares. +# Values larger than 26 will start to produce sharp cutoffs at cloud area corners. +cloud_radius (Cloud radius) int 12 1 62 + +# Whether node texture animations should be desynchronized per mapblock. +desynchronize_mapblock_texture_animation (Desynchronize block animation) bool true + +# Enables caching of facedir rotated meshes. +enable_mesh_cache (Mesh cache) bool false + +# Delay between mesh updates on the client in ms. Increasing this will slow +# down the rate of mesh updates, thus reducing jitter on slower clients. +mesh_generation_interval (Mapblock mesh generation delay) int 0 0 50 + +# Size of the MapBlock cache of the mesh generator. Increasing this will +# increase the cache hit %, reducing the data being copied from the main +# thread, thus reducing jitter. +meshgen_block_cache_size (Mapblock mesh generator's MapBlock cache size in MB) int 20 0 1000 + +# True = 256 +# False = 128 +# Usable to make minimap smoother on slower machines. +minimap_double_scan_height (Minimap scan height) bool true + +# Textures on a node may be aligned either to the node or to the world. +# The former mode suits better things like machines, furniture, etc., while +# the latter makes stairs and microblocks fit surroundings better. +# However, as this possibility is new, thus may not be used by older servers, +# this option allows enforcing it for certain node types. Note though that +# that is considered EXPERIMENTAL and may not work properly. +world_aligned_mode (World-aligned textures mode) enum enable disable,enable,force_solid,force_nodebox + +# World-aligned textures may be scaled to span several nodes. However, +# the server may not send the scale you want, especially if you use +# a specially-designed texture pack; with this option, the client tries +# to determine the scale automatically basing on the texture size. +# See also texture_min_size. +# Warning: This option is EXPERIMENTAL! +autoscale_mode (Autoscaling mode) enum disable disable,enable,force + +[**Font] + +font_bold (Font bold by default) bool false + +font_italic (Font italic by default) bool false + +# Shadow offset (in pixels) of the default font. If 0, then shadow will not be drawn. +font_shadow (Font shadow) int 1 0 65535 + +# Opaqueness (alpha) of the shadow behind the default font, between 0 and 255. +font_shadow_alpha (Font shadow alpha) int 127 0 255 + +# Font size of the default font where 1 unit = 1 pixel at 96 DPI +font_size (Font size) int 16 5 72 + +# For pixel-style fonts that do not scale well, this ensures that font sizes used +# with this font will always be divisible by this value, in pixels. For instance, +# a pixel font 16 pixels tall should have this set to 16, so it will only ever be +# sized 16, 32, 48, etc., so a mod requesting a size of 25 will get 32. +font_size_divisible_by (Font size divisible by) int 1 1 + +# Path to the default font. Must be a TrueType font. +# The fallback font will be used if the font cannot be loaded. +font_path (Regular font path) filepath fonts/Arimo-Regular.ttf + +font_path_bold (Bold font path) filepath fonts/Arimo-Bold.ttf +font_path_italic (Italic font path) filepath fonts/Arimo-Italic.ttf +font_path_bold_italic (Bold and italic font path) filepath fonts/Arimo-BoldItalic.ttf + +# Font size of the monospace font where 1 unit = 1 pixel at 96 DPI +mono_font_size (Monospace font size) int 16 5 72 + +# For pixel-style fonts that do not scale well, this ensures that font sizes used +# with this font will always be divisible by this value, in pixels. For instance, +# a pixel font 16 pixels tall should have this set to 16, so it will only ever be +# sized 16, 32, 48, etc., so a mod requesting a size of 25 will get 32. +mono_font_size_divisible_by (Monospace font size divisible by) int 1 1 + +# Path to the monospace font. Must be a TrueType font. +# This font is used for e.g. the console and profiler screen. +mono_font_path (Monospace font path) filepath fonts/Cousine-Regular.ttf + +mono_font_path_bold (Bold monospace font path) filepath fonts/Cousine-Bold.ttf +mono_font_path_italic (Italic monospace font path) filepath fonts/Cousine-Italic.ttf +mono_font_path_bold_italic (Bold and italic monospace font path) filepath fonts/Cousine-BoldItalic.ttf + +# Path of the fallback font. Must be a TrueType font. +# This font will be used for certain languages or if the default font is unavailable. +fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf + +[**Lighting] + +# Gradient of light curve at minimum light level. +# Controls the contrast of the lowest light levels. +lighting_alpha (Light curve low gradient) float 0.0 0.0 3.0 + +# Gradient of light curve at maximum light level. +# Controls the contrast of the highest light levels. +lighting_beta (Light curve high gradient) float 1.5 0.0 3.0 + +# Strength of light curve boost. +# The 3 'boost' parameters define a range of the light +# curve that is boosted in brightness. +lighting_boost (Light curve boost) float 0.2 0.0 0.4 + +# Center of light curve boost range. +# Where 0.0 is minimum light level, 1.0 is maximum light level. +lighting_boost_center (Light curve boost center) float 0.5 0.0 1.0 + +# Spread of light curve boost range. +# Controls the width of the range to be boosted. +# Standard deviation of the light curve boost Gaussian. +lighting_boost_spread (Light curve boost spread) float 0.2 0.0 0.4 + +[**Networking] + +# Prometheus listener address. +# If Minetest is compiled with ENABLE_PROMETHEUS option enabled, +# enable metrics listener for Prometheus on that address. +# Metrics can be fetched on http://127.0.0.1:30000/metrics +prometheus_listener_address (Prometheus listener address) string 127.0.0.1:30000 + +# Maximum size of the out chat queue. +# 0 to disable queueing and -1 to make the queue size unlimited. +max_out_chat_queue_size (Maximum size of the out chat queue) int 20 -1 32767 + +# Timeout for client to remove unused map data from memory, in seconds. +client_unload_unused_data_timeout (Mapblock unload timeout) float 600.0 0.0 + +# Maximum number of mapblocks for client to be kept in memory. +# Set to -1 for unlimited amount. +client_mapblock_limit (Mapblock limit) int 7500 -1 2147483647 + +# Whether to show the client debug info (has the same effect as hitting F5). +show_debug (Show debug info) bool false + +# Maximum number of blocks that are simultaneously sent per client. +# The maximum total count is calculated dynamically: +# max_total = ceil((#clients + max_users) * per_client / 4) +max_simultaneous_block_sends_per_client (Maximum simultaneous block sends per client) int 40 1 4294967295 + +# 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 (Delay in sending blocks after building) float 2.0 0.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 +# client number. +max_packets_per_iteration (Max. packets per iteration) int 1024 1 65535 + +# Compression level to use when sending mapblocks to the client. +# -1 - use default compression level +# 0 - least compression, fastest +# 9 - best compression, slowest +map_compression_level_net (Map Compression Level for Network Transfer) int -1 -1 9 + +[**Server] + +# Format of player chat messages. The following strings are valid placeholders: +# @name, @message, @timestamp (optional) +chat_message_format (Chat message format) string <@name> @message + +# If the execution of a chat command takes longer than this specified time in +# seconds, add the time information to the chat command message +chatcommand_msg_time_threshold (Chat command time message threshold) float 0.1 0.0 + +# A message to be displayed to all clients when the server shuts down. +kick_msg_shutdown (Shutdown message) string Server shutting down. + +# A message to be displayed to all clients when the server crashes. +kick_msg_crash (Crash message) string This server has experienced an internal error. You will now be disconnected. + +# Whether to ask clients to reconnect after a (Lua) crash. +# Set this to true if your server is set up to restart automatically. +ask_reconnect_on_crash (Ask to reconnect after crash) bool false + +[**Server/Env Performance] + +# Length of a server tick and the interval at which objects are generally updated over +# network, stated in seconds. +dedicated_server_step (Dedicated server step) float 0.09 0.0 + +# Whether players are shown to clients without any range limit. +# Deprecated, use the setting player_transfer_distance instead. +unlimited_player_transfer_distance (Unlimited player transfer distance) bool true + +# Defines the maximal player transfer distance in blocks (0 = unlimited). +player_transfer_distance (Player transfer distance) int 0 0 65535 + +# From how far clients know about objects, stated in mapblocks (16 nodes). +# +# Setting this larger than active_block_range will also cause the server +# to maintain active objects up to this distance in the direction the +# player is looking. (This can avoid mobs suddenly disappearing from view) +active_object_send_range_blocks (Active object send range) int 8 1 65535 + +# The radius of the volume of blocks around every player that is subject to the +# active block stuff, stated in mapblocks (16 nodes). +# In active blocks objects are loaded and ABMs run. +# This is also the minimum range in which active objects (mobs) are maintained. +# This should be configured together with active_object_send_range_blocks. +active_block_range (Active block range) int 4 1 65535 + +# From how far blocks are sent to clients, stated in mapblocks (16 nodes). +max_block_send_distance (Max block send distance) int 12 1 65535 + +# Maximum number of forceloaded mapblocks. +max_forceloaded_blocks (Maximum forceloaded blocks) int 16 0 + +# Interval of sending time of day to clients, stated in seconds. +time_send_interval (Time send interval) float 5.0 0.001 + +# Interval of saving important changes in the world, stated in seconds. +server_map_save_interval (Map save interval) float 5.3 0.001 + +# How long the server will wait before unloading unused mapblocks, stated in seconds. +# Higher value is smoother, but will use more RAM. +server_unload_unused_data_timeout (Unload unused server data) int 29 0 4294967295 + +# Maximum number of statically stored objects in a block. +max_objects_per_block (Maximum objects per block) int 256 1 65535 + +# Length of time between active block management cycles, stated in seconds. +active_block_mgmt_interval (Active block management interval) float 2.0 0.0 + +# Length of time between Active Block Modifier (ABM) execution cycles, stated in seconds. +abm_interval (ABM interval) float 1.0 0.0 + +# The time budget allowed for ABMs to execute on each step +# (as a fraction of the ABM Interval) +abm_time_budget (ABM time budget) float 0.2 0.1 0.9 + +# Length of time between NodeTimer execution cycles, stated in seconds. +nodetimer_interval (NodeTimer interval) float 0.2 0.0 + +# Max liquids processed per step. +liquid_loop_max (Liquid loop max) int 100000 1 4294967295 + +# The time (in seconds) that the liquids queue may grow beyond processing +# capacity until an attempt is made to decrease its size by dumping old queue +# items. A value of 0 disables the functionality. +liquid_queue_purge_time (Liquid queue purge time) int 0 0 65535 + +# Liquid update interval in seconds. +liquid_update (Liquid update tick) float 1.0 0.001 + +# 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 32767 + +# If enabled the server will perform map block occlusion culling based on +# on the eye position of the player. This can reduce the number of blocks +# sent to the client 50-80%. The client will not longer receive most invisible +# so that the utility of noclip mode is reduced. +server_side_occlusion_culling (Server side occlusion culling) bool true + +[**Mapgen] + # Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes). # WARNING!: There is no benefit, and there are several dangers, in # increasing this value above 5. # Reducing this value increases cave and dungeon density. # Altering this value is for special usage, leaving it unchanged is # recommended. -chunksize (Chunk size) int 5 +chunksize (Chunk size) int 5 1 10 # Dump the mapgen debug information. enable_mapgen_debug_info (Mapgen debug) bool false @@ -2193,21 +1916,438 @@ emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 1 # speed, but this may harm game performance by interfering with other # processes, especially in singleplayer and/or when running Lua code in # 'on_generated'. For many users the optimum setting may be '1'. -num_emerge_threads (Number of emerge threads) int 1 +num_emerge_threads (Number of emerge threads) int 1 0 32767 -[Online Content Repository] +[**cURL] -# The URL for the content repository -contentdb_url (ContentDB URL) string https://content.minetest.net +# Maximum time an interactive request (e.g. server list fetch) may take, stated in milliseconds. +curl_timeout (cURL interactive timeout) int 20000 100 2147483647 -# Comma-separated list of flags to hide in the content repository. -# "nonfree" can be used to hide packages which do not qualify as 'free software', -# as defined by the Free Software Foundation. -# You can also specify content ratings. -# These flags are independent from Minetest versions, -# so see a full list at https://content.minetest.net/help/content_flags/ -contentdb_flag_blacklist (ContentDB Flag Blacklist) string nonfree, desktop_default +# Limits number of parallel HTTP requests. Affects: +# - Media fetch if server uses remote_media setting. +# - Serverlist download and server announcement. +# - Downloads performed by main menu (e.g. mod manager). +# Only has an effect if compiled with cURL. +curl_parallel_limit (cURL parallel limit) int 8 1 2147483647 -# Maximum number of concurrent downloads. Downloads exceeding this limit will be queued. -# This should be lower than curl_parallel_limit. -contentdb_max_concurrent_downloads (ContentDB Max Concurrent Downloads) int 3 +# Maximum time a file download (e.g. a mod download) may take, stated in milliseconds. +curl_file_download_timeout (cURL file download timeout) int 300000 100 2147483647 + +[**Misc] + +# Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens. +screen_dpi (DPI) int 72 1 + +# Adjust the detected display density, used for scaling UI elements. +display_density_factor (Display Density Scaling Factor) float 1 0.5 5.0 + +# Windows systems only: Start Minetest with the command line window in the background. +# Contains the same information as the file debug.txt (default name). +enable_console (Enable console window) bool false + +# 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). +max_clearobjects_extra_loaded_blocks (Max. clearobjects extra blocks) int 4096 0 4294967295 + +# World directory (everything in the world is stored here). +# Not needed if starting from the main menu. +map-dir (Map directory) path + +# See https://www.sqlite.org/pragma.html#pragma_synchronous +sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2 + +# Compression level to use when saving mapblocks to disk. +# -1 - use default compression level +# 0 - least compression, fastest +# 9 - best compression, slowest +map_compression_level_disk (Map Compression Level for Disk Storage) int -1 -1 9 + +# Enable usage of remote media server (if provided by server). +# Remote servers offer a significantly faster way to download media (e.g. textures) +# when connecting to the server. +enable_remote_media_server (Connect to external media server) bool true + +# File in client/serverlist/ that contains your favorite servers displayed in the +# Multiplayer Tab. +serverlist_file (Serverlist file) string favoriteservers.json + + +[*Gamepads] + +# Enable joysticks. Requires a restart to take effect +enable_joysticks (Enable joysticks) bool false + +# The identifier of the joystick to use +joystick_id (Joystick ID) int 0 0 255 + +# The type of joystick +joystick_type (Joystick type) enum auto auto,generic,xbox,dragonrise_gamecube + +# 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 0.001 + +# The dead zone of the joystick +joystick_deadzone (Joystick dead zone) int 2048 0 65535 + +# The sensitivity of the joystick axes for moving the +# in-game view frustum around. +joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170.0 0.001 + + +[*Temporary Settings] + +# Path to texture directory. All textures are first searched from here. +texture_path (Texture path) path + +# Enables minimap. +enable_minimap (Minimap) bool true + +# Shape of the minimap. Enabled = round, disabled = square. +minimap_shape_round (Round minimap) bool true + +# Address to connect to. +# Leave this blank to start a local server. +# Note that the address field in the main menu overrides this setting. +address (Server address) string + +# Port to connect to (UDP). +# Note that the port field in the main menu overrides this setting. +remote_port (Remote port) int 30000 1 65535 + +# Default game when creating a new world. +# This will be overridden when creating a world from the main menu. +default_game (Default game) string minetest + +# Enable players getting damage and dying. +enable_damage (Damage) bool false + +# Enable creative mode for all players +creative_mode (Creative) bool false + +# Whether to allow players to damage and kill each other. +enable_pvp (Player versus player) bool true + +# Player is able to fly without being affected by gravity. +# This requires the "fly" privilege on the server. +free_move (Flying) bool false + +# If enabled, makes move directions relative to the player's pitch when flying or swimming. +pitch_move (Pitch move mode) bool false + +# Fast movement (via the "Aux1" key). +# This requires the "fast" privilege on the server. +fast_move (Fast movement) bool false + +# If enabled together with fly mode, player is able to fly through solid nodes. +# This requires the "noclip" privilege on the server. +noclip (Noclip) bool false + +# Continuous forward movement, toggled by autoforward key. +# Press the autoforward key again or the backwards movement to disable. +continuous_forward (Continuous forward) bool false + +# Formspec default background opacity (between 0 and 255). +formspec_default_bg_opacity (Formspec Default Background Opacity) int 140 0 255 + +# Formspec default background color (R,G,B). +formspec_default_bg_color (Formspec Default Background Color) string (0,0,0) + +# Whether to show technical names. +# Affects mods and texture packs in the Content and Select Mods menus, as well as +# setting names in All Settings. +# Controlled by the checkbox in the "All settings" menu. +show_technical_names (Show technical names) bool false + +# Enables the sound system. +# If disabled, this completely disables all sounds everywhere and the in-game +# sound controls will be non-functional. +# Changing this setting requires a restart. +enable_sound (Sound) bool true + +# Key for moving the player forward. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_forward (Forward key) key KEY_KEY_W + +# Key for moving the player backward. +# Will also disable autoforward, when active. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_backward (Backward key) key KEY_KEY_S + +# Key for moving the player left. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_left (Left key) key KEY_KEY_A + +# Key for moving the player right. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_right (Right key) key KEY_KEY_D + +# Key for jumping. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_jump (Jump key) key KEY_SPACE + +# Key for sneaking. +# Also used for climbing down and descending in water if aux1_descends is disabled. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_sneak (Sneak key) key KEY_LSHIFT + +# Key for digging. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_dig (Dig key) key KEY_LBUTTON + +# Key for placing. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_place (Place key) key KEY_RBUTTON + +# Key for opening the inventory. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_inventory (Inventory key) key KEY_KEY_I + +# Key for moving fast in fast mode. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_aux1 (Aux1 key) key KEY_KEY_E + +# Key for opening the chat window. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_chat (Chat key) key KEY_KEY_T + +# Key for opening the chat window to type commands. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_cmd (Command key) key / + +# Key for opening the chat window to type local commands. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_cmd_local (Command key) key . + +# Key for toggling unlimited view range. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_rangeselect (Range select key) key KEY_KEY_R + +# Key for toggling flying. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_freemove (Fly key) key KEY_KEY_K + +# Key for toggling pitch move mode. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_pitchmove (Pitch move key) key KEY_KEY_P + +# Key for toggling fast mode. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_fastmove (Fast key) key KEY_KEY_J + +# Key for toggling noclip mode. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_noclip (Noclip key) key KEY_KEY_H + +# Key for selecting the next item in the hotbar. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_hotbar_next (Hotbar next key) key KEY_KEY_N + +# Key for selecting the previous item in the hotbar. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_hotbar_previous (Hotbar previous key) key KEY_KEY_B + +# Key for muting the game. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_mute (Mute key) key KEY_KEY_M + +# Key for increasing the volume. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_increase_volume (Inc. volume key) key + +# Key for decreasing the volume. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_decrease_volume (Dec. volume key) key + +# Key for toggling autoforward. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_autoforward (Automatic forward key) key + +# Key for toggling cinematic mode. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_cinematic (Cinematic mode key) key + +# Key for toggling display of minimap. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_minimap (Minimap key) key KEY_KEY_V + +# Key for taking screenshots. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_screenshot (Screenshot) key KEY_F12 + +# Key for dropping the currently selected item. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_drop (Drop item key) key KEY_KEY_Q + +# Key to use view zoom when possible. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_zoom (View zoom key) key KEY_KEY_Z + +# Key for selecting the first hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot1 (Hotbar slot 1 key) key KEY_KEY_1 + +# Key for selecting the second hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot2 (Hotbar slot 2 key) key KEY_KEY_2 + +# Key for selecting the third hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot3 (Hotbar slot 3 key) key KEY_KEY_3 + +# Key for selecting the fourth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot4 (Hotbar slot 4 key) key KEY_KEY_4 + +# Key for selecting the fifth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot5 (Hotbar slot 5 key) key KEY_KEY_5 + +# Key for selecting the sixth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot6 (Hotbar slot 6 key) key KEY_KEY_6 + +# Key for selecting the seventh hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot7 (Hotbar slot 7 key) key KEY_KEY_7 + +# Key for selecting the eighth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot8 (Hotbar slot 8 key) key KEY_KEY_8 + +# Key for selecting the ninth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot9 (Hotbar slot 9 key) key KEY_KEY_9 + +# Key for selecting the tenth hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot10 (Hotbar slot 10 key) key KEY_KEY_0 + +# Key for selecting the 11th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot11 (Hotbar slot 11 key) key + +# Key for selecting the 12th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot12 (Hotbar slot 12 key) key + +# Key for selecting the 13th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot13 (Hotbar slot 13 key) key + +# Key for selecting the 14th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot14 (Hotbar slot 14 key) key + +# Key for selecting the 15th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot15 (Hotbar slot 15 key) key + +# Key for selecting the 16th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot16 (Hotbar slot 16 key) key + +# Key for selecting the 17th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot17 (Hotbar slot 17 key) key + +# Key for selecting the 18th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot18 (Hotbar slot 18 key) key + +# Key for selecting the 19th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot19 (Hotbar slot 19 key) key + +# Key for selecting the 20th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot20 (Hotbar slot 20 key) key + +# Key for selecting the 21st hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot21 (Hotbar slot 21 key) key + +# Key for selecting the 22nd hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot22 (Hotbar slot 22 key) key + +# Key for selecting the 23rd hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot23 (Hotbar slot 23 key) key + +# Key for selecting the 24th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot24 (Hotbar slot 24 key) key + +# Key for selecting the 25th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot25 (Hotbar slot 25 key) key + +# Key for selecting the 26th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot26 (Hotbar slot 26 key) key + +# Key for selecting the 27th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot27 (Hotbar slot 27 key) key + +# Key for selecting the 28th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot28 (Hotbar slot 28 key) key + +# Key for selecting the 29th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot29 (Hotbar slot 29 key) key + +# Key for selecting the 30th hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot30 (Hotbar slot 30 key) key + +# Key for selecting the 31st hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot31 (Hotbar slot 31 key) key + +# Key for selecting the 32nd hotbar slot. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_slot32 (Hotbar slot 32 key) key + +# Key for toggling the display of the HUD. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_toggle_hud (HUD toggle key) key KEY_F1 + +# Key for toggling the display of chat. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_toggle_chat (Chat toggle key) key KEY_F2 + +# Key for toggling the display of the large chat console. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_console (Large chat console key) key KEY_F10 + +# Key for toggling the display of fog. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_toggle_force_fog_off (Fog toggle key) key KEY_F3 + +# 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 + +# Key for toggling the display of debug info. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_toggle_debug (Debug info toggle key) key KEY_F5 + +# Key for toggling the display of the profiler. Used for development. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_toggle_profiler (Profiler toggle key) key KEY_F6 + +# Key for switching between first- and third-person camera. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_camera_mode (Toggle camera mode key) key KEY_KEY_C + +# Key for increasing the viewing range. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_increase_viewing_range_min (View range increase key) key + + +# Key for decreasing the viewing range. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_decrease_viewing_range_min (View range decrease key) key - |