1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-07-22 17:18:39 +00:00

Merge branch 'master' into master

This commit is contained in:
DustyBagel 2025-05-24 11:19:16 -05:00 committed by GitHub
commit 80c0410442
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
769 changed files with 137450 additions and 85683 deletions

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
-- Global menu data
menudata = {}
@ -34,7 +21,6 @@ function check_cache_age(key, max_age)
end
function core.on_before_close()
-- called before the menu is closed, either exit or to join a game
cache_settings:write()
end

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2018-24 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.
-- Luanti
-- Copyright (C) 2018-24 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
if not core.get_http_api then
return
@ -41,6 +28,7 @@ contentdb = {
REASON_DEPENDENCY = "dependency",
}
-- API documentation: https://content.luanti.org/help/api/
local function get_download_url(package, reason)
local base_url = core.settings:get("contentdb_url")
@ -182,14 +170,16 @@ function contentdb.get_package_by_id(id)
end
function contentdb.calculate_package_id(type, author, name)
local id = author:lower() .. "/"
local function strip_game_suffix(type, name)
if (type == nil or type == "game") and #name > 5 and name:sub(#name - 4) == "_game" then
id = id .. name:sub(1, #name - 5)
return name:sub(1, #name - 5)
else
id = id .. name
return name
end
return id
end
function contentdb.calculate_package_id(type, author, name)
return author:lower() .. "/" .. strip_game_suffix(type, name)
end
@ -398,7 +388,6 @@ local function fetch_pkgs()
local url = base_url ..
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
item = item:trim()
if item ~= "" then
@ -406,19 +395,11 @@ local function fetch_pkgs()
end
end
local languages
local current_language = core.get_language()
if current_language ~= "" then
languages = { current_language, "en;q=0.8" }
else
languages = { "en" }
end
local http = core.get_http_api()
local response = http.fetch_sync({
url = url,
extra_headers = {
"Accept-Language: " .. table.concat(languages, ", ")
core.get_http_accept_languages()
},
})
if not response.succeeded then
@ -448,7 +429,7 @@ function contentdb.set_packages_from_api(packages)
-- We currently don't support name changing
local suffix = "/" .. package.name
if alias:sub(-#suffix) == suffix then
contentdb.aliases[alias:lower()] = package.id
contentdb.aliases[strip_game_suffix(packages.type, alias:lower())] = package.id
end
end
end
@ -596,57 +577,54 @@ function contentdb.filter_packages(query, by_type)
end
function contentdb.get_full_package_info(package, callback)
assert(package)
if package.full_info then
callback(package.full_info)
return
end
local function fetch(params)
local version = core.get_version()
local base_url = core.settings:get("contentdb_url")
local languages
local current_language = core.get_language()
if current_language ~= "" then
languages = { current_language, "en;q=0.8" }
else
languages = { "en" }
local function get_package_info(key, path)
return function(package, callback)
assert(package)
if package[key] then
callback(package[key])
return
end
local url = base_url ..
"/api/packages/" .. params.package.url_part .. "/for-client/?" ..
"protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
"&engine_version=" .. core.urlencode(version.string) ..
"&formspec_version=" .. core.urlencode(core.get_formspec_version()) ..
"&include_images=false"
local http = core.get_http_api()
local response = http.fetch_sync({
url = url,
extra_headers = {
"Accept-Language: " .. table.concat(languages, ", ")
},
})
if not response.succeeded then
return nil
local function fetch(params)
local version = core.get_version()
local base_url = core.settings:get("contentdb_url")
local url = base_url ..
"/api/packages/" .. params.package.url_part .. params.path .. "?" ..
"protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
"&engine_version=" .. core.urlencode(version.string) ..
"&formspec_version=" .. core.urlencode(core.get_formspec_version()) ..
"&include_images=false"
local http = core.get_http_api()
local response = http.fetch_sync({
url = url,
extra_headers = {
core.get_http_accept_languages()
},
})
if not response.succeeded then
return nil
end
return core.parse_json(response.data)
end
return core.parse_json(response.data)
end
local function my_callback(value)
package[key] = value
callback(value)
end
local function my_callback(value)
package.full_info = value
callback(value)
end
if not core.handle_async(fetch, { package = package }, my_callback) then
core.log("error", "ERROR: async event failed")
callback(nil)
if not core.handle_async(fetch, { package = package, path = path }, my_callback) then
core.log("error", "ERROR: async event failed")
callback(nil)
end
end
end
contentdb.get_full_package_info = get_package_info("full_info", "/for-client/")
contentdb.get_package_reviews = get_package_info("reviews", "/for-client/reviews/")
function contentdb.get_formspec_padding()
-- Padding is increased on Android to account for notches
-- TODO: use Android API to determine size of cut outs

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2018-20 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.
-- Luanti
-- Copyright (C) 2018-20 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
if not core.get_http_api then
function create_contentdb_dlg()
@ -323,9 +310,17 @@ local function get_formspec(dlgdata)
})
local img_w = cell_h * 3 / 2
-- Use as much of the available space as possible (so no padding on the
-- right/bottom), but don't quite allow the text to touch the border.
local text_w = cell_w - img_w - 0.25 - 0.025
local text_h = cell_h - 0.25 - 0.025
local start_idx = (cur_page - 1) * num_per_page + 1
for i=start_idx, math.min(#contentdb.packages, start_idx+num_per_page-1) do
local package = contentdb.packages[i]
local text = core.colorize(mt_color_green, package.title) ..
core.colorize("#BFBFBF", " by " .. package.author) .. "\n" ..
package.short_description
table.insert_all(formspec, {
"container[",
@ -340,13 +335,14 @@ local function get_formspec(dlgdata)
"image[0,0;", img_w, ",", cell_h, ";",
core.formspec_escape(get_screenshot(package, package.thumbnail, 2)), "]",
"label[", img_w + 0.25 + 0.05, ",0.5;",
core.formspec_escape(
core.colorize(mt_color_green, package.title) ..
core.colorize("#BFBFBF", " by " .. package.author)), "]",
"label[", img_w + 0.25, ",0.25;", text_w, ",", text_h, ";",
core.formspec_escape(text), "]",
"textarea[", img_w + 0.25, ",0.75;", cell_w - img_w - 0.25, ",", cell_h - 0.75, ";;;",
core.formspec_escape(package.short_description), "]",
-- Add a tooltip in case the label overflows and the short description is cut off.
"tooltip[", img_w + 0.25, ",0.25;", text_w, ",", text_h, ";",
-- Text in tooltips doesn't wrap automatically, so we do it manually to
-- avoid everything being one long line.
core.formspec_escape(core.wrap_text(package.short_description, 80)), "]",
"style[view_", i, ";border=false]",
"style[view_", i, ":hovered;bgimg=", core.formspec_escape(defaulttexturedir .. "button_hover_semitrans.png"), "]",
@ -362,7 +358,7 @@ local function get_formspec(dlgdata)
end
table.insert_all(formspec, {
"container[", cell_w - 0.625,",", 0.25, "]",
"container[", cell_w - 0.625,",", 0.125, "]",
})
if package.downloading then

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2018-24 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.
-- Luanti
-- Copyright (C) 2018-24 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function is_still_visible(dlg)
local this = ui.find_by_name("install_dialog")

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2018-24 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.
-- Luanti
-- Copyright (C) 2018-24 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
function get_formspec(data)
local package = data.package

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2018-24 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.
-- Luanti
-- Copyright (C) 2018-24 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function get_info_formspec(size, padding, text)
@ -32,6 +19,7 @@ end
local function get_formspec(data)
local package = data.package
local window_padding = contentdb.get_formspec_padding()
local size = contentdb.get_formspec_size()
size.x = math.min(size.x, 20)
@ -42,7 +30,7 @@ local function get_formspec(data)
if not data.loading and not data.loading_error then
data.loading = true
contentdb.get_full_package_info(data.package, function(info)
contentdb.get_full_package_info(package, function(info)
data.loading = false
if info == nil then
@ -61,7 +49,7 @@ local function get_formspec(data)
-- check to see if that happened
if not data.info then
if data.loading_error then
return get_info_formspec(size, window_padding, fgettext("No packages could be retrieved"))
return get_info_formspec(size, window_padding, fgettext("Error loading package information"))
end
return get_info_formspec(size, window_padding, fgettext("Loading..."))
end
@ -103,15 +91,15 @@ local function get_formspec(data)
local left_button_rect = "0,0;2.875,1"
local right_button_rect = "3.125,0;2.875,1"
if data.package.downloading then
if package.downloading then
formspec[#formspec + 1] = "animated_image[5,0;1,1;downloading;"
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
elseif data.package.queued then
elseif package.queued then
formspec[#formspec + 1] = "style[queued;border=false]"
formspec[#formspec + 1] = "image_button[5,0;1,1;" .. core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "cdb_queued.png;queued;]"
elseif not data.package.path then
elseif not package.path then
formspec[#formspec + 1] = "style[install;bgcolor=green]"
formspec[#formspec + 1] = "button["
formspec[#formspec + 1] = right_button_rect
@ -119,7 +107,7 @@ local function get_formspec(data)
formspec[#formspec + 1] = fgettext("Install [$1]", info.download_size)
formspec[#formspec + 1] = "]"
else
if data.package.installed_release < data.package.release then
if package.installed_release < package.release then
-- The install_ action also handles updating
formspec[#formspec + 1] = "style[install;bgcolor=#28ccdf]"
formspec[#formspec + 1] = "button["
@ -137,10 +125,12 @@ local function get_formspec(data)
formspec[#formspec + 1] = "]"
end
local review_count = info.reviews.positive + info.reviews.neutral + info.reviews.negative
local current_tab = data.current_tab or 1
local tab_titles = {
fgettext("Description"),
fgettext("Information"),
fgettext("Reviews") .. core.formspec_escape(" [" .. review_count .. "]"),
}
local tab_body_height = bottom_buttons_y - 2.8
@ -162,8 +152,8 @@ local function get_formspec(data)
local winfo = core.get_window_info()
local fs_to_px = winfo.size.x / winfo.max_formspec_size.x
for i, ss in ipairs(info.screenshots) do
local path = get_screenshot(data.package, ss.url, 2)
hypertext = hypertext .. "<action name=\"ss_" .. i .. "\"><img name=\"" ..
local path = get_screenshot(package, ss.url, 2)
hypertext = hypertext .. "<action name=\"ss_".. i .. "\"><img name=\"" ..
core.hypertext_escape(path) .. "\" width=" .. (3 * fs_to_px) ..
" height=" .. (2 * fs_to_px) .. "></action>"
if i ~= #info.screenshots then
@ -194,22 +184,54 @@ local function get_formspec(data)
hypertext = hypertext .. "\n\n" .. info.long_description.body
-- Fix the path to blank.png. This is needed for bullet indentation.
hypertext = hypertext:gsub("<img name=\"?blank.png\"? ",
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "blank.png\" ")
table.insert_all(formspec, {
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
";desc;", core.formspec_escape(hypertext), "]",
})
elseif current_tab == 2 then
local hypertext = info.info_hypertext.head .. info.info_hypertext.body
table.insert_all(formspec, {
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
";info;", core.formspec_escape(hypertext), "]",
})
elseif current_tab == 3 then
if not package.reviews and not data.reviews_error and not data.reviews_loading then
data.reviews_loading = true
contentdb.get_package_reviews(package, function(reviews)
if not reviews then
data.reviews_error = true
end
ui.update()
end)
end
if package.reviews then
local hypertext = package.reviews.head .. package.reviews.body
-- Provide correct path to blank.png image. This is needed for bullet indentation.
hypertext = hypertext:gsub("<img name=\"?blank.png\"? ",
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "blank.png\" ")
-- Placeholders in reviews hypertext for icons
hypertext = hypertext:gsub("<thumbsup>",
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_thumb_up.png\" width=24>")
hypertext = hypertext:gsub("<thumbsdown>",
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_thumb_down.png\" width=24>")
hypertext = hypertext:gsub("<neutral>",
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_neutral.png\" width=24>")
table.insert_all(formspec, {
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
";reviews;", core.formspec_escape(hypertext), "]",
})
elseif data.reviews_error then
table.insert_all(formspec, {"label[2,2;", fgettext("Error loading reviews"), "]"} )
else
table.insert_all(formspec, {"label[2,2;", fgettext("Loading..."), "]"} )
end
else
error("Unknown tab " .. current_tab)
end
@ -269,9 +291,10 @@ local function handle_submit(this, fields)
end
if fields.open_contentdb then
local url = ("%s/packages/%s/?protocol_version=%d"):format(
core.settings:get("contentdb_url"), package.url_part,
core.get_max_supp_proto())
local version = core.get_version()
local url = core.settings:get("contentdb_url") .. "/packages/" .. package.url_part ..
"/?protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
"&engine_version=" .. core.urlencode(version.string)
core.open_url(url)
return true
end
@ -295,7 +318,8 @@ local function handle_submit(this, fields)
end
if handle_hypertext_event(this, fields.desc, info.long_description) or
handle_hypertext_event(this, fields.info, info.info_hypertext) then
handle_hypertext_event(this, fields.info, info.info_hypertext) or
(package.reviews and handle_hypertext_event(this, fields.reviews, package.reviews)) then
return true
end
end

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2023 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.
-- Luanti
-- Copyright (C) 2023 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local path = core.get_mainmenu_path() .. DIR_DELIM .. "content"

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2013 sapier
--
--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.
-- Luanti
-- Copyright (C) 2013 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
local function get_last_folder(text,count)

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2023-24 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.
-- Luanti
-- Copyright (C) 2023-24 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
-- Screenshot

View file

@ -1,19 +1,6 @@
--Luanti
--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.
-- Luanti
-- Copyright (C) 2022 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
local mods_dir = "/tmp/.minetest/mods"
local games_dir = "/tmp/.minetest/games"

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2023 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.
-- Luanti
-- Copyright (C) 2023 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
update_detector = {}

View file

@ -1,5 +1,5 @@
{
"#": "https://github.com/orgs/minetest/teams/engine/members",
"#": "https://github.com/orgs/luanti-org/teams/engine/members",
"core_developers": [
"Perttu Ahola (celeron55) <celeron55@gmail.com> [Project founder]",
"sfan5 <sfan5@live.de>",
@ -15,7 +15,8 @@
"Gregor Parzefall (grorp)",
"Lars Müller (luatic)",
"cx384",
"sfence"
"sfence",
"y5nw"
],
"previous_core_developers": [
"BlockMen",
@ -38,7 +39,7 @@
"Hugues Ross <hugues.ross@gmail.com>",
"Dmitry Kostenko (x2048) <codeforsmile@gmail.com>"
],
"#": "Currently only https://github.com/orgs/minetest/teams/triagers/members",
"#": "Currently only https://github.com/orgs/luanti-org/teams/triagers/members",
"core_team": [
"Zughy [Issue triager]",
"wsor [Issue triager]",
@ -46,25 +47,20 @@
],
"#": "For updating active/previous contributors, see the script in ./util/gather_git_credits.py",
"contributors": [
"JosiahWI",
"1F616EMO",
"y5nw",
"Erich Schubert",
"numzero",
"red-001 <red-001@outlook.ie>",
"David Heidelberg",
"Wuzzy",
"paradust7",
"HybridDog",
"Zemtzov7",
"kromka-chleba",
"AFCMS",
"chmodsayshello",
"OgelGames"
"wrrrzr",
"siliconsniffer",
"JosiahWI",
"veprogames",
"Miguel P.L",
"AFCMS"
],
"previous_contributors": [
"Ælla Chiana Moskopp (erle) <erle@dieweltistgarnichtso.net> [Logo]",
"numzero",
"red-001 <red-001@outlook.ie>",
"Giuseppe Bilotta",
"HybridDog",
"ClobberXD",
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
"MirceaKitsune <mirceakitsune@gmail.com>",
@ -77,6 +73,7 @@
"stujones11",
"Rogier <rogier777@gmail.com>",
"Gregory Currie (gregorycu)",
"paradust7",
"JacobF",
"Jeija <jeija@mesecons.net>"
]

View file

@ -21,7 +21,7 @@ local function clients_list_formspec(dialogdata)
"size[6,9.5]",
TOUCH_GUI and "padding[0.01,0.01]" or "",
"hypertext[0,0;6,1.5;;<global margin=5 halign=center valign=middle>",
fgettext("This is the list of clients connected to\n$1",
fgettext("Players connected to\n$1",
"<b>" .. core.hypertext_escape(servername) .. "</b>") .. "]",
"textlist[0.5,1.5;5,6.8;;" .. fmt_formspec_list(clients_list) .. "]",
"button[1.5,8.5;3,0.8;quit;OK]"
@ -32,7 +32,7 @@ end
local function clients_list_buttonhandler(this, fields)
if fields.quit then
this:delete()
this:delete()
return true
end
return false

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2013 sapier
--
--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.
-- Luanti
-- Copyright (C) 2013 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------
@ -299,7 +286,7 @@ local function handle_buttons(this, fields)
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")
worldfile:remove("load_mod_" .. mod.name)
end
elseif mod.enabled then
gamedata.errormessage = fgettext_ne("Failed to enable mo" ..

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function table_to_flags(ftable)
-- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves"

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function delete_world_formspec(dialogdata)

View file

@ -0,0 +1,108 @@
-- Luanti
-- SPDX-License-Identifier: LGPL-2.1-or-later
-- Modified based on dlg_reinstall_mtg.lua
-- Note that this is only needed for migrating from <5.11 to 5.12.
local doc_url = "https://docs.luanti.org/for-players/controls/"
local SETTING_NAME = "no_keycode_migration_warning"
local function get_formspec(dialogdata)
local markup = table.concat({
"<big>" .. hgettext("Keybindings changed") .. "</big>",
hgettext("The input handling system was reworked in Luanti 5.12.0."),
hgettext("As a result, your keybindings may have been changed."),
hgettext("Check out the key settings or refer to the documentation:"),
("<action name='doc_url'><style color='cyan' hovercolor='orangered'>%s</style></action>"):format(doc_url),
}, "\n")
return table.concat({
"formspec_version[6]",
"size[12,7]",
"hypertext[0.5,0.5;11,4.7;text;", core.formspec_escape(markup), "]",
"container[0.5,5.7]",
"button[0,0;4,0.8;dismiss;", fgettext("Close"), "]",
"button[4.5,0;6.5,0.8;reconfigure;", fgettext("Open settings"), "]",
"container_end[]",
})
end
local function close_dialog(this)
cache_settings:set_bool(SETTING_NAME, true)
this:delete()
end
local function buttonhandler(this, fields)
if fields.reconfigure then
close_dialog(this)
local maintab = ui.find_by_name("maintab")
local dlg = create_settings_dlg("controls_keyboard_and_mouse")
dlg:set_parent(maintab)
maintab:hide()
dlg:show()
return true
end
if fields.dismiss then
close_dialog(this)
return true
end
if fields.text == "action:doc_url" then
core.open_url(doc_url)
end
end
local function eventhandler(event)
if event == "DialogShow" then
mm_game_theme.set_engine()
return true
elseif event == "MenuQuit" then
-- Don't allow closing the dialog with ESC, but still allow exiting
-- Luanti
core.close()
return true
end
return false
end
local function create_rebind_keys_dlg()
local dlg = dialog_create("dlg_rebind_keys", get_formspec,
buttonhandler, eventhandler)
return dlg
end
function migrate_keybindings()
-- Show migration dialog if the user upgraded from an earlier version
-- and this has not yet been shown before, *or* if keys settings had to be changed
if core.is_first_run then
cache_settings:set_bool(SETTING_NAME, true)
end
local has_migration = not cache_settings:get_bool(SETTING_NAME)
-- normalize all existing key settings, this converts them from KEY_KEY_C to SYSTEM_SCANCODE_6
local settings = core.settings:to_table()
for name, value in pairs(settings) do
if name:match("^keymap_") then
local normalized = core.normalize_keycode(value)
if value ~= normalized then
has_migration = true
core.settings:set(name, normalized)
end
end
end
if not has_migration then
return
end
local maintab = ui.find_by_name("maintab")
local dlg = create_rebind_keys_dlg()
dlg:set_parent(maintab)
maintab:hide()
dlg:show()
ui.update()
end

View file

@ -1,19 +1,6 @@
--Luanti
--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.
-- Luanti
-- Copyright (C) 2022 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------

View file

@ -1,25 +1,12 @@
--Luanti
--Copyright (C) 2023 Gregor Parzefall
--
--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.
-- Luanti
-- Copyright (C) 2023 Gregor Parzefall
-- SPDX-License-Identifier: LGPL-2.1-or-later
---- IMPORTANT ----
-- This whole file can be removed after a while.
-- It was only directly useful for upgrades from 5.7.0 to 5.8.0, but
-- maybe some odd fellow directly upgrades from 5.6.1 to 5.9.0 in the future...
-- see <https://github.com/minetest/minetest/pull/13850> in case it's not obvious
-- see <https://github.com/luanti-org/luanti/pull/13850> in case it's not obvious
---- ----
local SETTING_NAME = "no_mtg_notification"
@ -67,10 +54,10 @@ end
local function get_formspec(dialogdata)
local markup = table.concat({
"<big>", fgettext("Minetest Game is no longer installed by default"), "</big>\n",
fgettext("For a long time, Luanti shipped with a default game called \"Minetest Game\". " ..
"<big>", hgettext("Minetest Game is no longer installed by default"), "</big>\n",
hgettext("For a long time, Luanti shipped with a default game called \"Minetest Game\". " ..
"Since version 5.8.0, Luanti ships without a default game."), "\n",
fgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
hgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
})
return table.concat({

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
--------------------------------------------------------------------------------

View file

@ -0,0 +1,105 @@
-- Luanti
-- Copyright (C) 2024 cx384
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function get_formspec(dialogdata)
local TOUCH_GUI = core.settings:get_bool("touch_gui")
local server = dialogdata.server
local group_by_prefix = dialogdata.group_by_prefix
local expand_all = dialogdata.expand_all
-- A wrongly behaving server may send ill formed mod names
table.sort(server.mods)
local cells = {}
if group_by_prefix then
local function get_prefix(mod)
return mod:match("[^_]*")
end
local count = {}
for _, mod in ipairs(server.mods) do
local prefix = get_prefix(mod)
count[prefix] = (count[prefix] or 0) + 1
end
local last_prefix
local function add_row(depth, mod)
table.insert(cells, ("%d"):format(depth))
table.insert(cells, mod)
end
for i, mod in ipairs(server.mods) do
local prefix = get_prefix(mod)
if last_prefix == prefix then
add_row(1, mod)
elseif count[prefix] > 1 then
add_row(0, prefix)
add_row(1, mod)
else
add_row(0, mod)
end
last_prefix = prefix
end
else
cells = table.copy(server.mods)
end
for i, cell in ipairs(cells) do
cells[i] = core.formspec_escape(cell)
end
cells = table.concat(cells, ",")
local heading
if server.gameid then
heading = fgettext("The $1 server uses a game called $2 and the following mods:",
"<b>" .. core.hypertext_escape(server.name) .. "</b>",
"<style font=mono>" .. core.hypertext_escape(server.gameid) .. "</style>")
else
heading = fgettext("The $1 server uses the following mods:",
"<b>" .. core.hypertext_escape(server.name) .. "</b>")
end
local formspec = {
"formspec_version[8]",
"size[8,9.5]",
TOUCH_GUI and "padding[0.01,0.01]" or "",
"hypertext[0,0;8,1.5;;<global margin=5 halign=center valign=middle>", heading, "]",
"tablecolumns[", group_by_prefix and
(expand_all and "indent;text" or "tree;text") or "text", "]",
"table[0.5,1.5;7,6.8;mods;", cells, "]",
"checkbox[0.5,8.7;group_by_prefix;", fgettext("Group by prefix"), ";",
group_by_prefix and "true" or "false", "]",
group_by_prefix and ("checkbox[0.5,9.15;expand_all;" .. fgettext("Expand all") .. ";" ..
(expand_all and "true" or "false") .. "]") or "",
"button[5.5,8.5;2,0.8;quit;OK]"
}
return table.concat(formspec, "")
end
local function buttonhandler(this, fields)
if fields.quit then
this:delete()
return true
end
if fields.group_by_prefix then
this.data.group_by_prefix = core.is_yes(fields.group_by_prefix)
return true
end
if fields.expand_all then
this.data.expand_all = core.is_yes(fields.expand_all)
return true
end
return false
end
function create_server_list_mods_dialog(server)
local retval = dialog_create("dlg_server_list_mods",
get_formspec,
buttonhandler,
nil)
retval.data.group_by_prefix = false
retval.data.expand_all = false
retval.data.server = server
return retval
end

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2013 sapier
--
--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.
-- Luanti
-- Copyright (C) 2013 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
mm_game_theme = {}

View file

@ -1,27 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
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"
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
MAIN_TAB_W = 15.5
MAIN_TAB_H = 7.1
@ -35,6 +14,7 @@ local basepath = core.get_builtin_path()
defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
DIR_DELIM .. "pack" .. DIR_DELIM
dofile(basepath .. "common" .. DIR_DELIM .. "menu.lua")
dofile(basepath .. "common" .. DIR_DELIM .. "filterlist.lua")
dofile(basepath .. "fstk" .. DIR_DELIM .. "buttonbar.lua")
dofile(basepath .. "fstk" .. DIR_DELIM .. "dialog.lua")
@ -47,7 +27,7 @@ dofile(menupath .. DIR_DELIM .. "game_theme.lua")
dofile(menupath .. DIR_DELIM .. "content" .. DIR_DELIM .. "init.lua")
dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
dofile(menupath .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
dofile(basepath .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.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")
@ -55,7 +35,9 @@ dofile(menupath .. DIR_DELIM .. "dlg_register.lua")
dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua")
dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua")
dofile(menupath .. DIR_DELIM .. "dlg_rebind_keys.lua")
dofile(menupath .. DIR_DELIM .. "dlg_clients_list.lua")
dofile(menupath .. DIR_DELIM .. "dlg_server_list_mods.lua")
local tabs = {
content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"),
@ -131,6 +113,7 @@ local function init_globals()
ui.update()
check_reinstall_mtg()
migrate_keybindings()
check_new_version()
end

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2020 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.
-- Luanti
-- Copyright (C) 2020 rubenwardy
-- SPDX-License-Identifier: LGPL-2.1-or-later
serverlistmgr = {
-- continent code we detected for ourselves

View file

@ -1,435 +0,0 @@
--Luanti
--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 make = {}
-- This file defines various component constructors, of the form:
--
-- make.component(setting)
--
-- `setting` is a table representing the settingtype.
--
-- A component is a table with the following:
--
-- * `full_width`: (Optional) true if the component shouldn't reserve space for info / reset.
-- * `info_text`: (Optional) string, informational text shown in an info icon.
-- * `setting`: (Optional) the setting.
-- * `max_w`: (Optional) maximum width, `avail_w` will never exceed this.
-- * `resettable`: (Optional) if this is true, a reset button is shown.
-- * `get_formspec = function(self, avail_w)`:
-- * `avail_w` is the available width for the component.
-- * Returns `fs, used_height`.
-- * `fs` is a string for the formspec.
-- Components should be relative to `0,0`, and not exceed `avail_w` or the returned `used_height`.
-- * `used_height` is the space used by components in `fs`.
-- * `on_submit = function(self, fields, parent)`:
-- * `fields`: submitted formspec fields
-- * `parent`: the fstk element for the settings UI, use to show dialogs
-- * Return true if the event was handled, to prevent future components receiving it.
local function get_label(setting)
local show_technical_names = core.settings:get_bool("show_technical_names")
if not show_technical_names and setting.readable_name then
return fgettext(setting.readable_name)
end
return setting.name
end
local function is_valid_number(value)
return type(value) == "number" and not (value ~= value or value >= math.huge or value <= -math.huge)
end
function make.heading(text)
return {
full_width = true,
get_formspec = function(self, avail_w)
return ("label[0,0.6;%s]box[0,0.9;%f,0.05;#ccc6]"):format(core.formspec_escape(text), avail_w), 1.2
end,
}
end
function make.note(text)
return {
full_width = true,
get_formspec = function(self, avail_w)
-- Assuming label height 0.4:
-- Position at y=0 to eat 0.2 of the padding above, leave 0.05.
-- The returned used_height doesn't include padding.
return ("label[0,0;%s]"):format(core.colorize("#bbb", core.formspec_escape(text))), 0.2
end,
}
end
--- Used for string and numeric style fields
---
--- @param converter Function to coerce values from strings.
--- @param validator Validator function, optional. Returns true when valid.
--- @param stringifier Function to convert values to strings, optional.
local function make_field(converter, validator, stringifier)
return function(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = core.settings:get(setting.name) or setting.default
self.resettable = core.settings:has(setting.name)
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
avail_w - 1.5, setting.name, get_label(setting), core.formspec_escape(value))
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name)
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
return fs, 1.1
end,
on_submit = function(self, fields)
if fields["set_" .. setting.name] or fields.key_enter_field == setting.name then
local value = converter(fields[setting.name])
if value == nil or (validator and not validator(value)) then
return true
end
if setting.min then
value = math.max(value, setting.min)
end
if setting.max then
value = math.min(value, setting.max)
end
core.settings:set(setting.name, (stringifier or tostring)(value))
return true
end
end,
}
end
end
make.float = make_field(tonumber, is_valid_number, function(x)
local str = tostring(x)
if str:match("^[+-]?%d+$") then
str = str .. ".0"
end
return str
end)
make.int = make_field(function(x)
local value = tonumber(x)
return value and math.floor(value)
end, is_valid_number)
make.string = make_field(tostring, nil)
function make.bool(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = core.settings:get_bool(setting.name, core.is_yes(setting.default))
self.resettable = core.settings:has(setting.name)
local fs = ("checkbox[0,0.25;%s;%s;%s]"):format(
setting.name, get_label(setting), tostring(value))
return fs, 0.5
end,
on_submit = function(self, fields)
if fields[setting.name] == nil then
return false
end
core.settings:set_bool(setting.name, core.is_yes(fields[setting.name]))
return true
end,
}
end
function make.enum(setting)
return {
info_text = setting.comment,
setting = setting,
max_w = 4.5,
get_formspec = function(self, avail_w)
local value = core.settings:get(setting.name) or setting.default
self.resettable = core.settings:has(setting.name)
local labels = setting.option_labels or {}
local items = {}
for i, option in ipairs(setting.values) do
items[i] = core.formspec_escape(labels[option] or option)
end
local selected_idx = table.indexof(setting.values, value)
local fs = "label[0,0.1;" .. get_label(setting) .. "]"
fs = fs .. ("dropdown[0,0.3;%f,0.8;%s;%s;%d;true]"):format(
avail_w, setting.name, table.concat(items, ","), selected_idx, value)
return fs, 1.1
end,
on_submit = function(self, fields)
local old_value = core.settings:get(setting.name) or setting.default
local idx = tonumber(fields[setting.name]) or 0
local value = setting.values[idx]
if value == nil or value == old_value then
return false
end
core.settings:set(setting.name, value)
return true
end,
}
end
local function make_path(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = core.settings:get(setting.name) or setting.default
self.resettable = core.settings:has(setting.name)
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
avail_w - 3, setting.name, get_label(setting), core.formspec_escape(value))
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 3, "pick_" .. setting.name, fgettext("Browse"))
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
return fs, 1.1
end,
on_submit = function(self, fields)
local dialog_name = "dlg_path_" .. setting.name
if fields["pick_" .. setting.name] then
local is_file = setting.type ~= "path"
core.show_path_select_dialog(dialog_name,
is_file and fgettext_ne("Select file") or fgettext_ne("Select directory"), is_file)
return true
end
if fields[dialog_name .. "_accepted"] then
local value = fields[dialog_name .. "_accepted"]
if value ~= nil then
core.settings:set(setting.name, value)
end
return true
end
if fields["set_" .. setting.name] or fields.key_enter_field == setting.name then
local value = fields[setting.name]
if value ~= nil then
core.settings:set(setting.name, value)
end
return true
end
end,
}
end
if PLATFORM == "Android" then
-- The Irrlicht file picker doesn't work on Android.
make.path = make.string
make.filepath = make.string
else
make.path = make_path
make.filepath = make_path
end
function make.v3f(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = vector.from_string(core.settings:get(setting.name) or setting.default)
self.resettable = core.settings:has(setting.name)
-- Allocate space for "Set" button
avail_w = avail_w - 1
local fs = "label[0,0.1;" .. get_label(setting) .. "]"
local field_width = (avail_w - 3*0.25) / 3
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
0, field_width, setting.name .. "_x", "X", value.x)
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
field_width + 0.25, field_width, setting.name .. "_y", "Y", value.y)
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
2 * (field_width + 0.25), field_width, setting.name .. "_z", "Z", value.z)
fs = fs .. ("button[%f,0.6;1,0.8;%s;%s]"):format(avail_w, "set_" .. setting.name, fgettext("Set"))
return fs, 1.4
end,
on_submit = function(self, fields)
if fields["set_" .. setting.name] or
fields.key_enter_field == setting.name .. "_x" or
fields.key_enter_field == setting.name .. "_y" or
fields.key_enter_field == setting.name .. "_z" then
local x = tonumber(fields[setting.name .. "_x"])
local y = tonumber(fields[setting.name .. "_y"])
local z = tonumber(fields[setting.name .. "_z"])
if is_valid_number(x) and is_valid_number(y) and is_valid_number(z) then
core.settings:set(setting.name, vector.new(x, y, z):to_string())
else
core.log("error", "Invalid vector: " .. dump({x, y, z}))
end
return true
end
end,
}
end
function make.flags(setting)
local checkboxes = {}
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local fs = {
"label[0,0.1;" .. get_label(setting) .. "]",
}
self.resettable = core.settings:has(setting.name)
checkboxes = {}
for _, name in ipairs(setting.possible) do
checkboxes[name] = false
end
local function apply_flags(flag_string, what)
local prefixed_flags = {}
for _, name in ipairs(flag_string:split(",")) do
prefixed_flags[name:trim()] = true
end
for _, name in ipairs(setting.possible) do
local enabled = prefixed_flags[name]
local disabled = prefixed_flags["no" .. name]
if enabled and disabled then
core.log("warning", "Flag " .. name .. " in " .. what .. " " ..
setting.name .. " both enabled and disabled, ignoring")
elseif enabled then
checkboxes[name] = true
elseif disabled then
checkboxes[name] = false
end
end
end
-- First apply the default, which is necessary since flags
-- which are not overridden may be missing from the value.
apply_flags(setting.default, "default for setting")
local value = core.settings:get(setting.name)
if value then
apply_flags(value, "setting")
end
local columns = math.max(math.floor(avail_w / 2.5), 1)
local column_width = avail_w / columns
local x = 0
local y = 0.55
for _, possible in ipairs(setting.possible) do
if x >= avail_w then
x = 0
y = y + 0.5
end
local is_checked = checkboxes[possible]
fs[#fs + 1] = ("checkbox[%f,%f;%s;%s;%s]"):format(
x, y, setting.name .. "_" .. possible,
core.formspec_escape(possible), tostring(is_checked))
x = x + column_width
end
return table.concat(fs, ""), y + 0.25
end,
on_submit = function(self, fields)
local changed = false
for name, _ in pairs(checkboxes) do
local value = fields[setting.name .. "_" .. name]
if value ~= nil then
checkboxes[name] = core.is_yes(value)
changed = true
end
end
if changed then
local values = {}
for _, name in ipairs(setting.possible) do
if checkboxes[name] then
table.insert(values, name)
else
table.insert(values, "no" .. name)
end
end
core.settings:set(setting.name, table.concat(values, ","))
end
return changed
end
}
end
local function make_noise_params(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
-- The "defaults" noise parameter flag doesn't reset a noise
-- setting to its default value, so we offer a regular reset button.
self.resettable = core.settings:has(setting.name)
local fs = "label[0,0.4;" .. get_label(setting) .. "]" ..
("button[%f,0;2.5,0.8;%s;%s]"):format(avail_w - 2.5, "edit_" .. setting.name, fgettext("Edit"))
return fs, 0.8
end,
on_submit = function(self, fields, tabview)
if fields["edit_" .. setting.name] then
local dlg = create_change_mapgen_flags_dlg(setting)
dlg:set_parent(tabview)
tabview:hide()
dlg:show()
return true
end
end,
}
end
make.noise_params_2d = make_noise_params
make.noise_params_3d = make_noise_params
return make

View file

@ -1,252 +0,0 @@
--Luanti
--Copyright (C) 2015 PilzAdam
--
--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 checkboxes = {}
local function flags_to_table(flags)
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
end
local function get_current_np_group(setting)
local value = core.settings:get_np_group(setting.name)
if value == nil then
return setting.values
end
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_formspec(dialogdata)
local setting = dialogdata.setting
-- Final formspec will be created at the end of this function
-- Default values below, may be changed depending on setting type
local width = 10
local height = 2
local description_height = 1.5
local t = get_current_np_group(setting)
local dimension = 3
if setting.type == "noise_params_2d" then
dimension = 2
end
local fields = {}
local function add_field(x, name, label, value)
fields[#fields + 1] = ("field[%f,%f;3.3,1;%s;%s;%s]"):format(
x, height, name, label, core.formspec_escape(value or "")
)
end
-- First row
height = height + 0.3
add_field(0.3, "te_offset", fgettext("Offset"), t[1])
add_field(3.6, "te_scale", fgettext("Scale"), t[2])
add_field(6.9, "te_seed", fgettext("Seed"), t[6])
height = height + 1.1
-- Second row
add_field(0.3, "te_spreadx", fgettext("X spread"), t[3])
if dimension == 3 then
add_field(3.6, "te_spready", fgettext("Y spread"), t[4])
else
fields[#fields + 1] = "label[4," .. height - 0.2 .. ";" ..
fgettext("2D Noise") .. "]"
end
add_field(6.9, "te_spreadz", fgettext("Z spread"), t[5])
height = height + 1.1
-- Third row
add_field(0.3, "te_octaves", fgettext("Octaves"), t[7])
add_field(3.6, "te_persist", fgettext("Persistence"), t[8])
add_field(6.9, "te_lacun", fgettext("Lacunarity"), t[9])
height = height + 1.1
local enabled_flags = flags_to_table(t[10])
local flags = {}
for _, name in ipairs(enabled_flags) do
-- Index by name, to avoid iterating over all enabled_flags for every possible flag.
flags[name] = true
end
for _, name in ipairs(setting.flags) do
local checkbox_name = "cb_" .. name
local is_enabled = flags[name] == true -- to get false if nil
checkboxes[checkbox_name] = is_enabled
end
local formspec = table.concat(fields)
.. "checkbox[0.5," .. height - 0.6 .. ";cb_defaults;"
--[[~ "defaults" is a noise parameter flag.
It describes the default processing options
for noise settings in the settings menu. ]]
.. fgettext("defaults") .. ";" -- defaults
.. tostring(flags["defaults"] == true) .. "]" -- to get false if nil
.. "checkbox[5," .. height - 0.6 .. ";cb_eased;"
--[[~ "eased" is a noise parameter flag.
It is used to make the map smoother and
can be enabled in noise settings in
the settings menu. ]]
.. fgettext("eased") .. ";" -- eased
.. tostring(flags["eased"] == true) .. "]"
.. "checkbox[5," .. height - 0.15 .. ";cb_absvalue;"
--[[~ "absvalue" is a noise parameter flag.
It is short for "absolute value".
It can be enabled in noise settings in
the settings menu. ]]
.. fgettext("absvalue") .. ";" -- absvalue
.. tostring(flags["absvalue"] == true) .. "]"
height = height + 1
-- Box good, textarea bad. Calculate textarea size from box.
local function create_textfield(size, label, text, bg_color)
local textarea = {
x = size.x + 0.3,
y = size.y,
w = size.w + 0.25,
h = size.h * 1.16 + 0.12
}
return ("box[%f,%f;%f,%f;%s]textarea[%f,%f;%f,%f;;%s;%s]"):format(
size.x, size.y, size.w, size.h, bg_color or "#000",
textarea.x, textarea.y, textarea.w, textarea.h,
core.formspec_escape(label), core.formspec_escape(text)
)
end
-- When there's an error: Shrink description textarea and add error below
if dialogdata.error_message then
local error_box = {
x = 0,
y = description_height - 0.4,
w = width - 0.25,
h = 0.5
}
formspec = formspec ..
create_textfield(error_box, "", dialogdata.error_message, "#600")
description_height = description_height - 0.75
end
-- Get description field
local description_box = {
x = 0,
y = 0.2,
w = width - 0.25,
h = description_height
}
local setting_name = setting.name
if setting.readable_name then
setting_name = fgettext_ne(setting.readable_name) ..
" (" .. setting.name .. ")"
end
local comment_text
if setting.comment == "" then
comment_text = fgettext_ne("(No description of setting given)")
else
comment_text = fgettext_ne(setting.comment)
end
return (
"size[" .. width .. "," .. height + 0.25 .. ",true]" ..
create_textfield(description_box, setting_name, comment_text) ..
formspec ..
"button[" .. width / 2 - 2.5 .. "," .. height - 0.4 .. ";2.5,1;btn_done;" ..
fgettext("Save") .. "]" ..
"button[" .. width / 2 .. "," .. height - 0.4 .. ";2.5,1;btn_cancel;" ..
fgettext("Cancel") .. "]"
)
end
local function buttonhandler(this, fields)
local setting = this.data.setting
if fields["btn_done"] or fields["key_enter"] then
local np_flags = {}
for _, name in ipairs(setting.flags) do
if checkboxes["cb_" .. name] then
table.insert(np_flags, name)
end
end
checkboxes = {}
if setting.type == "noise_params_2d" then
fields["te_spready"] = fields["te_spreadz"]
end
local new_value = {
offset = fields["te_offset"],
scale = fields["te_scale"],
spread = {
x = fields["te_spreadx"],
y = fields["te_spready"],
z = fields["te_spreadz"]
},
seed = fields["te_seed"],
octaves = fields["te_octaves"],
persistence = fields["te_persist"],
lacunarity = fields["te_lacun"],
flags = table.concat(np_flags, ", ")
}
core.settings:set_np_group(setting.name, new_value)
core.settings:write()
this:delete()
return true
end
if fields["btn_cancel"] then
this:delete()
return true
end
for name, value in pairs(fields) do
if name:sub(1, 3) == "cb_" then
checkboxes[name] = core.is_yes(value)
return false -- Don't update the formspec!
end
end
return false
end
function create_change_mapgen_flags_dlg(setting)
assert(type(setting) == "table")
local retval = dialog_create("dlg_change_mapgen_flags",
get_formspec,
buttonhandler,
nil)
retval.data.setting = setting
return retval
end

View file

@ -1,775 +0,0 @@
--Luanti
--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 component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "components.lua")
local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "shadows_component.lua")
local loaded = false
local full_settings
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
local all_pages = {}
local page_by_id = {}
local filtered_pages = all_pages
local filtered_page_by_id = page_by_id
local function get_setting_info(name)
for _, entry in ipairs(full_settings) do
if entry.type ~= "category" and entry.name == name then
return entry
end
end
return nil
end
local function add_page(page)
assert(type(page.id) == "string")
assert(type(page.title) == "string")
assert(page.section == nil or type(page.section) == "string")
assert(type(page.content) == "table")
assert(not page_by_id[page.id], "Page " .. page.id .. " already registered")
all_pages[#all_pages + 1] = page
page_by_id[page.id] = page
return page
end
local function load_settingtypes()
local page = nil
local section = nil
local function ensure_page_started()
if not page then
page = add_page({
id = (section or "general"):lower():gsub(" ", "_"),
title = section or fgettext_ne("General"),
section = section,
content = {},
})
end
end
for _, entry in ipairs(full_settings) do
if entry.type == "category" then
if entry.level == 0 then
section = entry.name
page = nil
elseif entry.level == 1 then
page = {
id = ((section and section .. "_" or "") .. entry.name):lower():gsub(" ", "_"),
title = entry.readable_name or entry.name,
section = section,
content = {},
}
page = add_page(page)
elseif entry.level == 2 then
ensure_page_started()
page.content[#page.content + 1] = {
heading = fgettext_ne(entry.readable_name or entry.name),
}
end
else
ensure_page_started()
page.content[#page.content + 1] = entry.name
end
end
end
local function load()
if loaded then
return
end
loaded = true
full_settings = settingtypes.parse_config_file(false, true)
local change_keys = {
query_text = "Controls",
requires = {
touch_controls = false,
},
get_formspec = function(self, avail_w)
local btn_w = math.min(avail_w, 3)
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8
end,
on_submit = function(self, fields)
if fields.btn_change_keys then
core.show_keys_menu()
end
end,
}
local touchscreen_layout = {
query_text = "Touchscreen layout",
requires = {
touchscreen = true,
},
get_formspec = function(self, avail_w)
local btn_w = math.min(avail_w, 6)
return ("button[0,0;%f,0.8;btn_touch_layout;%s]"):format(btn_w, fgettext("Touchscreen layout")), 0.8
end,
on_submit = function(self, fields)
if fields.btn_touch_layout then
core.show_touchscreen_layout()
end
end,
}
add_page({
id = "accessibility",
title = fgettext_ne("Accessibility"),
content = {
"language",
{ heading = fgettext_ne("General") },
"font_size",
"chat_font_size",
"gui_scaling",
"hud_scaling",
"show_nametag_backgrounds",
{ heading = fgettext_ne("Chat") },
"console_height",
"console_alpha",
"console_color",
{ heading = fgettext_ne("Controls") },
"autojump",
"safe_dig_and_place",
{ heading = fgettext_ne("Movement") },
"arm_inertia",
"view_bobbing_amount",
"fall_bobbing_amount",
},
})
load_settingtypes()
table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
-- insert after "touch_controls"
table.insert(page_by_id.controls_touchscreen.content, 2, touchscreen_layout)
do
local content = page_by_id.graphics_and_audio_effects.content
local idx = table.indexof(content, "enable_dynamic_shadows")
table.insert(content, idx, shadows_component)
idx = table.indexof(content, "enable_auto_exposure") + 1
local note = component_funcs.note(fgettext_ne("(The game will need to enable automatic exposure as well)"))
note.requires = get_setting_info("enable_auto_exposure").requires
table.insert(content, idx, note)
idx = table.indexof(content, "enable_bloom") + 1
note = component_funcs.note(fgettext_ne("(The game will need to enable bloom as well)"))
note.requires = get_setting_info("enable_bloom").requires
table.insert(content, idx, note)
idx = table.indexof(content, "enable_volumetric_lighting") + 1
note = component_funcs.note(fgettext_ne("(The game will need to enable volumetric lighting as well)"))
note.requires = get_setting_info("enable_volumetric_lighting").requires
table.insert(content, idx, note)
end
-- These must not be translated, as they need to show in the local
-- language no matter the user's current language.
-- This list must be kept in sync with src/unsupported_language_list.txt.
get_setting_info("language").option_labels = {
[""] = fgettext_ne("(Use system language)"),
--ar = " [ar]", blacklisted
be = "Беларуская [be]",
bg = "Български [bg]",
ca = "Català [ca]",
cs = "Česky [cs]",
cy = "Cymraeg [cy]",
da = "Dansk [da]",
de = "Deutsch [de]",
--dv = " [dv]", blacklisted
el = "Ελληνικά [el]",
en = "English [en]",
eo = "Esperanto [eo]",
es = "Español [es]",
et = "Eesti [et]",
eu = "Euskara [eu]",
fi = "Suomi [fi]",
fil = "Wikang Filipino [fil]",
fr = "Français [fr]",
gd = "Gàidhlig [gd]",
gl = "Galego [gl]",
--he = " [he]", blacklisted
--hi = " [hi]", blacklisted
hu = "Magyar [hu]",
id = "Bahasa Indonesia [id]",
it = "Italiano [it]",
ja = "日本語 [ja]",
jbo = "Lojban [jbo]",
kk = "Қазақша [kk]",
--kn = " [kn]", blacklisted
ko = "한국어 [ko]",
ky = "Kırgızca / Кыргызча [ky]",
lt = "Lietuvių [lt]",
lv = "Latviešu [lv]",
mn = "Монгол [mn]",
mr = "मराठी [mr]",
ms = "Bahasa Melayu [ms]",
--ms_Arab = " [ms_Arab]", blacklisted
nb = "Norsk Bokmål [nb]",
nl = "Nederlands [nl]",
nn = "Norsk Nynorsk [nn]",
oc = "Occitan [oc]",
pl = "Polski [pl]",
pt = "Português [pt]",
pt_BR = "Português do Brasil [pt_BR]",
ro = "Română [ro]",
ru = "Русский [ru]",
sk = "Slovenčina [sk]",
sl = "Slovenščina [sl]",
sr_Cyrl = "Српски [sr_Cyrl]",
sr_Latn = "Srpski (Latinica) [sr_Latn]",
sv = "Svenska [sv]",
sw = "Kiswahili [sw]",
--th = " [th]", blacklisted
tr = "Türkçe [tr]",
tt = "Tatarça [tt]",
uk = "Українська [uk]",
vi = "Tiếng Việt [vi]",
zh_CN = "中文 (简体) [zh_CN]",
zh_TW = "正體中文 (繁體) [zh_TW]",
}
get_setting_info("touch_controls").option_labels = {
["auto"] = fgettext_ne("Auto"),
["true"] = fgettext_ne("Enabled"),
["false"] = fgettext_ne("Disabled"),
}
end
-- See if setting matches keywords
local function get_setting_match_weight(entry, query_keywords)
local setting_score = 0
for _, keyword in ipairs(query_keywords) do
if string.find(entry.name:lower(), keyword, 1, true) then
setting_score = setting_score + 1
end
if entry.readable_name and
string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then
setting_score = setting_score + 1
end
if entry.comment and
string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then
setting_score = setting_score + 1
end
end
return setting_score
end
local function filter_page_content(page, query_keywords)
if #query_keywords == 0 then
return page.content, 0
end
local retval = {}
local i = 1
local max_weight = 0
for _, content in ipairs(page.content) do
if type(content) == "string" then
local setting = get_setting_info(content)
assert(setting, "Unknown setting: " .. content)
local weight = get_setting_match_weight(setting, query_keywords)
if weight > 0 then
max_weight = math.max(max_weight, weight)
retval[i] = content
i = i + 1
end
elseif type(content) == "table" and content.query_text then
for _, keyword in ipairs(query_keywords) do
if string.find(fgettext(content.query_text), keyword, 1, true) then
max_weight = math.max(max_weight, 1)
retval[i] = content
i = i + 1
break
end
end
end
end
return retval, max_weight
end
local function update_filtered_pages(query)
filtered_pages = {}
filtered_page_by_id = {}
local query_keywords = {}
for word in query:lower():gmatch("%S+") do
table.insert(query_keywords, word)
end
local best_page = nil
local best_page_weight = -1
for _, page in ipairs(all_pages) do
local content, page_weight = filter_page_content(page, query_keywords)
if page_has_contents(page, content) then
local new_page = table.copy(page)
new_page.content = content
filtered_pages[#filtered_pages + 1] = new_page
filtered_page_by_id[new_page.id] = new_page
if page_weight > best_page_weight then
best_page = new_page
best_page_weight = page_weight
end
end
end
return best_page and best_page.id or nil
end
local function check_requirements(name, requires)
if requires == nil then
return true
end
local video_driver = core.get_active_driver()
local touch_support = core.irrlicht_device_supports_touch()
local touch_controls = core.settings:get("touch_controls")
local special = {
android = PLATFORM == "Android",
desktop = PLATFORM ~= "Android",
touch_support = touch_support,
-- When touch_controls is "auto", we don't know which input method will
-- be used, so we show settings for both.
touchscreen = touch_support and (touch_controls == "auto" or core.is_yes(touch_controls)),
keyboard_mouse = not touch_support or (touch_controls == "auto" or not core.is_yes(touch_controls)),
opengl = (video_driver == "opengl" or video_driver == "opengl3"),
gles = video_driver:sub(1, 5) == "ogles",
}
for req_key, req_value in pairs(requires) do
if special[req_key] == nil then
local required_setting = get_setting_info(req_key)
if required_setting == nil then
core.log("warning", "Unknown setting " .. req_key .. " required by " .. (name or "???"))
end
local actual_value = core.settings:get_bool(req_key,
required_setting and core.is_yes(required_setting.default))
if actual_value ~= req_value then
return false
end
elseif special[req_key] ~= req_value then
return false
end
end
return true
end
function page_has_contents(page, actual_content)
local is_advanced =
page.id:sub(1, #"client_and_server") == "client_and_server" or
page.id:sub(1, #"mapgen") == "mapgen" or
page.id:sub(1, #"advanced") == "advanced"
local show_advanced = core.settings:get_bool("show_advanced")
if is_advanced and not show_advanced then
return false
end
for _, item in ipairs(actual_content) do
if item == false or item.heading then --luacheck: ignore
-- skip
elseif type(item) == "string" then
local setting = get_setting_info(item)
assert(setting, "Unknown setting: " .. item)
if check_requirements(setting.name, setting.requires) then
return true
end
elseif item.get_formspec then
if check_requirements(item.id, item.requires) then
return true
end
else
error("Unknown content in page: " .. dump(item))
end
end
return false
end
local function build_page_components(page)
-- Filter settings based on requirements
local content = {}
local last_heading
for _, item in ipairs(page.content) do
if item == false then --luacheck: ignore
-- skip
elseif item.heading then
last_heading = item
else
local name, requires
if type(item) == "string" then
local setting = get_setting_info(item)
assert(setting, "Unknown setting: " .. item)
name = setting.name
requires = setting.requires
elseif item.get_formspec then
name = item.id
requires = item.requires
else
error("Unknown content in page: " .. dump(item))
end
if check_requirements(name, requires) then
if last_heading then
content[#content + 1] = last_heading
last_heading = nil
end
content[#content + 1] = item
end
end
end
-- Create components
local retval = {}
for i, item in ipairs(content) do
if type(item) == "string" then
local setting = get_setting_info(item)
local component_func = component_funcs[setting.type]
assert(component_func, "Unknown setting type: " .. setting.type)
retval[i] = component_func(setting)
elseif item.get_formspec then
retval[i] = item
elseif item.heading then
retval[i] = component_funcs.heading(item.heading)
end
end
return retval
end
local formspec_show_hack = false
local function get_formspec(dialogdata)
local page_id = dialogdata.page_id or "accessibility"
local page = filtered_page_by_id[page_id]
local extra_h = 1 -- not included in tabsize.height
local tabsize = {
width = core.settings:get_bool("touch_gui") and 16.5 or 15.5,
height = core.settings:get_bool("touch_gui") and (10 - extra_h) or 12,
}
local scrollbar_w = core.settings:get_bool("touch_gui") and 0.6 or 0.4
local left_pane_width = core.settings:get_bool("touch_gui") and 4.5 or 4.25
local left_pane_padding = 0.25
local search_width = left_pane_width + scrollbar_w - (0.75 * 2)
local back_w = 3
local checkbox_w = (tabsize.width - back_w - 2*0.2) / 2
local show_technical_names = core.settings:get_bool("show_technical_names")
local show_advanced = core.settings:get_bool("show_advanced")
formspec_show_hack = not formspec_show_hack
local fs = {
"formspec_version[6]",
"size[", tostring(tabsize.width), ",", tostring(tabsize.height + extra_h), "]",
core.settings:get_bool("touch_gui") and "padding[0.01,0.01]" or "",
"bgcolor[#0000]",
-- HACK: this is needed to allow resubmitting the same formspec
formspec_show_hack and " " or "",
"box[0,0;", tostring(tabsize.width), ",", tostring(tabsize.height), ";#0000008C]",
("button[0,%f;%f,0.8;back;%s]"):format(
tabsize.height + 0.2, back_w, fgettext("Back")),
("box[%f,%f;%f,0.8;#0000008C]"):format(
back_w + 0.2, tabsize.height + 0.2, checkbox_w),
("checkbox[%f,%f;show_technical_names;%s;%s]"):format(
back_w + 2*0.2, tabsize.height + 0.6,
fgettext("Show technical names"), tostring(show_technical_names)),
("box[%f,%f;%f,0.8;#0000008C]"):format(
back_w + 2*0.2 + checkbox_w, tabsize.height + 0.2, checkbox_w),
("checkbox[%f,%f;show_advanced;%s;%s]"):format(
back_w + 3*0.2 + checkbox_w, tabsize.height + 0.6,
fgettext("Show advanced settings"), tostring(show_advanced)),
"field[0.25,0.25;", tostring(search_width), ",0.75;search_query;;",
core.formspec_escape(dialogdata.query or ""), "]",
"field_enter_after_edit[search_query;true]",
"container[", tostring(search_width + 0.25), ", 0.25]",
"image_button[0,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
"image_button[0.75,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";search_clear;]",
"tooltip[search;", fgettext("Search"), "]",
"tooltip[search_clear;", fgettext("Clear"), "]",
"container_end[]",
("scroll_container[0.25,1.25;%f,%f;leftscroll;vertical;0.1;0]"):format(
left_pane_width, tabsize.height - 1.5),
"style_type[button;border=false;bgcolor=#3333]",
"style_type[button:hover;border=false;bgcolor=#6663]",
}
local y = 0
local last_section = nil
for _, other_page in ipairs(filtered_pages) do
if other_page.section ~= last_section then
fs[#fs + 1] = ("label[0.1,%f;%s]"):format(
y + 0.41, core.colorize("#ff0", fgettext(other_page.section)))
last_section = other_page.section
y = y + 0.82
end
fs[#fs + 1] = ("box[0,%f;%f,0.8;%s]"):format(
y, left_pane_width-left_pane_padding, other_page.id == page_id and "#467832FF" or "#3339")
fs[#fs + 1] = ("button[0,%f;%f,0.8;page_%s;%s]")
:format(y, left_pane_width-left_pane_padding, other_page.id, fgettext(other_page.title))
y = y + 0.82
end
if #filtered_pages == 0 then
fs[#fs + 1] = "label[0.1,0.41;"
fs[#fs + 1] = fgettext("No results")
fs[#fs + 1] = "]"
end
fs[#fs + 1] = "scroll_container_end[]"
if y >= tabsize.height - 1.25 then
fs[#fs + 1] = ("scrollbar[%f,1.25;%f,%f;vertical;leftscroll;%f]"):format(
left_pane_width + 0.25, scrollbar_w, tabsize.height - 1.5, dialogdata.leftscroll or 0)
end
fs[#fs + 1] = "style_type[button;border=;bgcolor=]"
if not dialogdata.components then
dialogdata.components = page and build_page_components(page) or {}
end
local right_pane_width = tabsize.width - left_pane_width - 0.375 - 2*scrollbar_w - 0.25
fs[#fs + 1] = ("scroll_container[%f,0;%f,%f;rightscroll;vertical;0.1;0.25]"):format(
tabsize.width - right_pane_width - scrollbar_w, right_pane_width, tabsize.height)
y = 0.25
for i, comp in ipairs(dialogdata.components) do
fs[#fs + 1] = ("container[0,%f]"):format(y)
local avail_w = right_pane_width - 0.25
if not comp.full_width then
avail_w = avail_w - 1.4
end
if comp.max_w then
avail_w = math.min(avail_w, comp.max_w)
end
local comp_fs, used_h = comp:get_formspec(avail_w)
fs[#fs + 1] = comp_fs
fs[#fs + 1] = "style_type[image_button;border=false;padding=]"
local show_reset = comp.resettable and comp.setting
local show_info = comp.info_text and comp.info_text ~= ""
if show_reset or show_info then
-- ensure there's enough space for reset/info
used_h = math.max(used_h, 0.5)
end
local info_reset_y = used_h / 2 - 0.25
if show_reset then
local default = comp.setting.default
local reset_tooltip = default and
fgettext("Reset setting to default ($1)", tostring(default)) or
fgettext("Reset setting to default")
fs[#fs + 1] = ("image_button[%f,%f;0.5,0.5;%s;%s;]"):format(
right_pane_width - 1.4, info_reset_y, reset_icon_path, "reset_" .. i)
fs[#fs + 1] = ("tooltip[%s;%s]"):format("reset_" .. i, reset_tooltip)
end
if show_info then
local info_x = right_pane_width - 0.75
fs[#fs + 1] = ("image[%f,%f;0.5,0.5;%s]"):format(info_x, info_reset_y, info_icon_path)
fs[#fs + 1] = ("tooltip[%f,%f;0.5,0.5;%s]"):format(info_x, info_reset_y, fgettext(comp.info_text))
end
fs[#fs + 1] = "style_type[image_button;border=;padding=]"
fs[#fs + 1] = "container_end[]"
if used_h > 0 then
y = y + used_h + 0.25
end
end
fs[#fs + 1] = "scroll_container_end[]"
if y >= tabsize.height then
fs[#fs + 1] = ("scrollbar[%f,0;%f,%f;vertical;rightscroll;%f]"):format(
tabsize.width - scrollbar_w, scrollbar_w, tabsize.height, dialogdata.rightscroll or 0)
end
return table.concat(fs, "")
end
-- On Android, closing the app via the "Recents screen" won't result in a clean
-- exit, discarding any setting changes made by the user.
-- To avoid that, we write the settings file in more cases on Android.
function write_settings_early()
if PLATFORM == "Android" then
core.settings:write()
end
end
local function regenerate_page_list(dialogdata)
local suggested_page_id = update_filtered_pages(dialogdata.query)
dialogdata.components = nil
if not filtered_page_by_id[dialogdata.page_id] then
dialogdata.leftscroll = 0
dialogdata.rightscroll = 0
dialogdata.page_id = suggested_page_id
end
end
local function buttonhandler(this, fields)
local dialogdata = this.data
dialogdata.leftscroll = core.explode_scrollbar_event(fields.leftscroll).value or dialogdata.leftscroll
dialogdata.rightscroll = core.explode_scrollbar_event(fields.rightscroll).value or dialogdata.rightscroll
dialogdata.query = fields.search_query
if fields.back then
this:delete()
return true
end
if fields.show_technical_names ~= nil then
local value = core.is_yes(fields.show_technical_names)
core.settings:set_bool("show_technical_names", value)
write_settings_early()
return true
end
if fields.show_advanced ~= nil then
local value = core.is_yes(fields.show_advanced)
core.settings:set_bool("show_advanced", value)
write_settings_early()
regenerate_page_list(dialogdata)
return true
end
if fields.search or fields.key_enter_field == "search_query" then
dialogdata.components = nil
dialogdata.leftscroll = 0
dialogdata.rightscroll = 0
dialogdata.page_id = update_filtered_pages(dialogdata.query)
return true
end
if fields.search_clear then
dialogdata.query = ""
dialogdata.components = nil
dialogdata.leftscroll = 0
dialogdata.rightscroll = 0
dialogdata.page_id = update_filtered_pages("")
return true
end
for _, page in ipairs(all_pages) do
if fields["page_" .. page.id] then
dialogdata.page_id = page.id
dialogdata.components = nil
dialogdata.rightscroll = 0
return true
end
end
local function after_setting_change(comp)
write_settings_early()
if comp.setting and comp.setting.name == "touch_controls" then
-- Changing the "touch_controls" setting may result in a different
-- page list.
regenerate_page_list(dialogdata)
else
-- Clear components so they regenerate
dialogdata.components = nil
end
end
for i, comp in ipairs(dialogdata.components) do
if comp.on_submit and comp:on_submit(fields, this) then
after_setting_change(comp)
return true
end
if comp.setting and fields["reset_" .. i] then
core.settings:remove(comp.setting.name)
after_setting_change(comp)
return true
end
end
return false
end
local function eventhandler(event)
if event == "DialogShow" then
-- Don't show the header image behind the dialog.
mm_game_theme.set_engine(true)
return true
end
if event == "FullscreenChange" then
-- Refresh the formspec to keep the fullscreen checkbox up to date.
ui.update()
return true
end
return false
end
function create_settings_dlg()
load()
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
dlg.data.page_id = update_filtered_pages("")
return dlg
end

View file

@ -1,139 +0,0 @@
local concat = table.concat
local insert = table.insert
local sprintf = string.format
local rep = string.rep
local minetest_example_header = [[
# This file contains a list of all available settings and their default value for minetest.conf
# By default, all the settings are commented and not functional.
# Uncomment settings by removing the preceding #.
# minetest.conf is read by default from:
# ../minetest.conf
# ../../minetest.conf
# Any other path can be chosen by passing the path as a parameter
# to the program, eg. "luanti.exe --config ../minetest.conf.example".
# Further documentation:
# https://wiki.luanti.org/
]]
local group_format_template = [[
# %s = {
# offset = %s,
# scale = %s,
# spread = (%s, %s, %s),
# seed = %s,
# octaves = %s,
# persistence = %s,
# lacunarity = %s,
# flags =%s
# }
]]
local function create_minetest_conf_example(settings)
local result = { minetest_example_header }
for _, entry in ipairs(settings) do
if entry.type == "category" then
if entry.level == 0 then
insert(result, "#\n# " .. entry.name .. "\n#\n\n")
else
insert(result, rep("#", entry.level))
insert(result, "# " .. entry.name .. "\n\n")
end
else
local group_format = false
if entry.noise_params and entry.values then
if entry.type == "noise_params_2d" or entry.type == "noise_params_3d" then
group_format = true
end
end
if entry.comment ~= "" then
for _, comment_line in ipairs(entry.comment:split("\n", true)) do
if comment_line == "" then
insert(result, "#\n")
else
insert(result, "# " .. comment_line .. "\n")
end
end
end
if entry.type == "key" then
local line = "See https://github.com/minetest/irrlicht/blob/master/include/Keycodes.h"
insert(result, "# " .. line .. "\n")
end
insert(result, "# type: " .. entry.type)
if entry.min then
insert(result, " min: " .. entry.min)
end
if entry.max then
insert(result, " max: " .. entry.max)
end
if entry.values and entry.noise_params == nil then
insert(result, " values: " .. concat(entry.values, ", "))
end
if entry.possible then
insert(result, " possible values: " .. concat(entry.possible, ", "))
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],
flags))
else
local append
if entry.default ~= "" then
append = " " .. entry.default
end
insert(result, sprintf("# %s =%s\n\n", entry.name, append or ""))
end
end
end
return concat(result)
end
local translation_file_header = [[
// This file is automatically generated
// 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() {]]
local function create_translation_file(settings)
local result = { translation_file_header }
for _, entry in ipairs(settings) do
if entry.type == "category" then
insert(result, sprintf("\tgettext(%q);", entry.name))
else
if entry.readable_name then
insert(result, sprintf("\tgettext(%q);", entry.readable_name))
end
if entry.comment ~= "" then
local comment_escaped = entry.comment:gsub("\n", "\\n")
comment_escaped = comment_escaped:gsub("\"", "\\\"")
insert(result, "\tgettext(\"" .. comment_escaped .. "\");")
end
end
end
insert(result, "}\n")
return concat(result, "\n")
end
local file = assert(io.open("minetest.conf.example", "w"))
file:write(create_minetest_conf_example(settingtypes.parse_config_file(true, false)))
file:close()
file = assert(io.open("src/settings_translation_file.cpp", "w"))
-- If 'minetest.conf.example' appears in the 'bin' folder, the line below may have to be
-- used instead. The file will also appear in the 'bin' folder.
--file = assert(io.open("settings_translation_file.cpp", "w"))
-- We don't want hidden settings to be translated, so we set read_all to false.
file:write(create_translation_file(settingtypes.parse_config_file(false, false)))
file:close()

View file

@ -1,28 +0,0 @@
--Luanti
--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 path = core.get_mainmenu_path() .. DIR_DELIM .. "settings"
dofile(path .. DIR_DELIM .. "settingtypes.lua")
dofile(path .. DIR_DELIM .. "dlg_change_mapgen_flags.lua")
dofile(path .. DIR_DELIM .. "dlg_settings.lua")
-- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
-- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
-- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
-- dofile(path .. DIR_DELIM .. "generate_from_settingtypes.lua")

View file

@ -1,507 +0,0 @@
--Luanti
--Copyright (C) 2015 PilzAdam
--
--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.
settingtypes = {}
-- A Setting type is a table with the following keys:
--
-- name: Identifier
-- readable_name: Readable title
-- type: Category
--
-- name = mod.name,
-- readable_name = mod.title,
-- level = 1,
-- type = "category", "int", "string", ""
-- }
local FILENAME = "settingtypes.txt"
local CHAR_CLASSES = {
SPACE = "[%s]",
VARIABLE = "[%w_%-%.]",
INTEGER = "[+-]?[%d]",
FLOAT = "[+-]?[%d%.]",
FLAGS = "[%w_%-%.,]",
}
local function flags_to_table(flags)
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
end
-- returns error message, or nil
local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
-- strip carriage returns (CR, /r)
line = line:gsub("\r", "")
-- comment
local comment_match = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
if comment_match then
settings.current_comment[#settings.current_comment + 1] = comment_match
return
end
-- clear current_comment so only comments directly above a setting are bound to it
-- but keep a local reference to it for variables in the current line
local current_comment = settings.current_comment
settings.current_comment = {}
-- empty lines
if line:match("^" .. CHAR_CLASSES.SPACE .. "*$") then
return
end
-- category
local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
if category then
local category_level = stars:len() + base_level
if settings.current_hide_level then
if settings.current_hide_level < category_level then
-- Skip this category, it's inside a hidden category.
return
else
-- The start of this category marks the end of a hidden category.
settings.current_hide_level = nil
end
end
if not read_all and category:sub(1, 5) == "Hide:" then
-- This category is hidden.
settings.current_hide_level = category_level
return
end
table.insert(settings, {
name = category,
level = category_level,
type = "category",
})
return
end
if settings.current_hide_level then
-- Ignore this line, we're inside a hidden category.
return
end
-- settings
local first_part, name, readable_name, setting_type = line:match("^"
-- this first capture group matches the whole first part,
-- so we can later strip it from the rest of the line
.. "("
.. "([" .. CHAR_CLASSES.VARIABLE .. "+)" -- variable name
.. CHAR_CLASSES.SPACE .. "*"
.. "%(([^%)]*)%)" -- readable name
.. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
.. CHAR_CLASSES.SPACE .. "*"
.. ")")
if not first_part then
return "Invalid line"
end
if name:match("secure%.[.]*") and not allow_secure then
return "Tried to add \"secure.\" setting"
end
local requires = {}
local last_line = #current_comment > 0 and current_comment[#current_comment]:trim()
if last_line and last_line:lower():sub(1, 9) == "requires:" then
local parts = last_line:sub(10):split(",")
current_comment[#current_comment] = nil
for _, part in ipairs(parts) do
part = part:trim()
local value = true
if part:sub(1, 1) == "!" then
value = false
part = part:sub(2):trim()
end
requires[part] = value
end
end
if readable_name == "" then
readable_name = nil
end
local remaining_line = line:sub(first_part:len() + 1)
local comment = table.concat(current_comment, "\n"):trim()
if setting_type == "int" then
local default, min, max = remaining_line:match("^"
-- first int is required, the last 2 are optional
.. "(" .. CHAR_CLASSES.INTEGER .. "+)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.INTEGER .. "*)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.INTEGER .. "*)"
.. "$")
if not default or not tonumber(default) then
return "Invalid integer setting"
end
min = tonumber(min)
max = tonumber(max)
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "int",
default = default,
min = min,
max = max,
requires = requires,
comment = comment,
})
return
end
if setting_type == "string"
or setting_type == "key" or setting_type == "v3f" then
local default = remaining_line:match("^(.*)$")
if not default then
return "Invalid string setting"
end
if setting_type == "key" and not read_all then
-- ignore key type if read_all is false
return
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = setting_type,
default = default,
requires = requires,
comment = comment,
})
return
end
if setting_type == "noise_params_2d"
or setting_type == "noise_params_3d" then
local default = remaining_line:match("^(.*)$")
if not default then
return "Invalid string setting"
end
local values = {}
local ti = 1
local index = 1
for match in default:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
index = default:find("[+-]?[%d.-e]+", index) + match:len()
table.insert(values, match)
ti = ti + 1
if ti > 9 then
break
end
end
index = default:find("[^, ]", index)
local flags = ""
if index then
flags = default:sub(index)
end
table.insert(values, flags)
table.insert(settings, {
name = name,
readable_name = readable_name,
type = setting_type,
default = default,
default_table = {
offset = values[1],
scale = values[2],
spread = {
x = values[3],
y = values[4],
z = values[5]
},
seed = values[6],
octaves = values[7],
persistence = values[8],
lacunarity = values[9],
flags = values[10]
},
values = values,
requires = requires,
comment = comment,
noise_params = true,
flags = flags_to_table("defaults,eased,absvalue")
})
return
end
if setting_type == "bool" then
if remaining_line ~= "false" and remaining_line ~= "true" then
return "Invalid boolean setting"
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "bool",
default = remaining_line,
requires = requires,
comment = comment,
})
return
end
if setting_type == "float" then
local default, min, max = remaining_line:match("^"
-- first float is required, the last 2 are optional
.. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLOAT .. "*)"
.."$")
if not default or not tonumber(default) then
return "Invalid float setting"
end
min = tonumber(min)
max = tonumber(max)
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "float",
default = default,
min = min,
max = max,
requires = requires,
comment = comment,
})
return
end
if setting_type == "enum" then
local default, values = remaining_line:match("^"
-- first value (default) may be empty (i.e. is optional)
.. "(" .. CHAR_CLASSES.VARIABLE .. "*)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLAGS .. "+)"
.. "$")
if not default or values == "" then
return "Invalid enum setting"
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "enum",
default = default,
values = values:split(",", true),
requires = requires,
comment = comment,
})
return
end
if setting_type == "path" or setting_type == "filepath" then
local default = remaining_line:match("^(.*)$")
if not default then
return "Invalid path setting"
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = setting_type,
default = default,
requires = requires,
comment = comment,
})
return
end
if setting_type == "flags" then
local default, possible = remaining_line:match("^"
-- first value (default) may be empty (i.e. is optional)
-- this is implemented by making the last value optional, and
-- swapping them around if it turns out empty.
.. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLAGS .. "*)"
.. "$")
if not default or not possible then
return "Invalid flags setting"
end
if possible == "" then
possible = default
default = ""
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "flags",
default = default,
possible = flags_to_table(possible),
requires = requires,
comment = comment,
})
return
end
return "Invalid setting type \"" .. setting_type .. "\""
end
local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
-- store this helper variable in the table so it's easier to pass to parse_setting_line()
result.current_comment = {}
result.current_hide_level = nil
local line = file:read("*line")
while line do
local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
if error_msg then
core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
end
line = file:read("*line")
end
result.current_comment = nil
result.current_hide_level = nil
end
--- Returns table of setting types
--
-- @param read_all Whether to ignore certain setting types for GUI or not
-- @parse_mods Whether to parse settingtypes.txt in mods and games
function settingtypes.parse_config_file(read_all, parse_mods)
local settings = {}
do
local builtin_path = core.get_builtin_path() .. FILENAME
local file = io.open(builtin_path, "r")
if not file then
core.log("error", "Can't load " .. FILENAME)
return settings
end
parse_single_file(file, builtin_path, read_all, settings, 0, true)
file:close()
end
if parse_mods then
-- Parse games
local games_category_initialized = false
for _, game in ipairs(pkgmgr.games) do
local path = game.path .. DIR_DELIM .. FILENAME
local file = io.open(path, "r")
if file then
if not games_category_initialized then
fgettext_ne("Content: Games") -- not used, but needed for xgettext
table.insert(settings, {
name = "Content: Games",
level = 0,
type = "category",
})
games_category_initialized = true
end
table.insert(settings, {
name = game.path,
readable_name = game.title,
level = 1,
type = "category",
})
parse_single_file(file, path, read_all, settings, 2, false)
file:close()
end
end
-- Parse mods
pkgmgr.load_all()
local mods_category_initialized = false
local mods = pkgmgr.global_mods:get_list()
table.sort(mods, function(a, b) return a.name < b.name end)
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("Content: Mods") -- not used, but needed for xgettext
table.insert(settings, {
name = "Content: Mods",
level = 0,
type = "category",
})
mods_category_initialized = true
end
table.insert(settings, {
name = mod.path,
readable_name = mod.title or mod.name,
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 = {}
pkgmgr.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.path,
readable_name = mod.title or mod.name,
level = 1,
type = "category",
})
parse_single_file(file, path, read_all, settings, 2, false)
file:close()
end
end
end
return settings
end

View file

@ -1,120 +0,0 @@
--Luanti
--Copyright (C) 2021-2 x2048
--Copyright (C) 2022-3 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 shadow_levels_labels = {
fgettext("Disabled"),
fgettext("Very Low"),
fgettext("Low"),
fgettext("Medium"),
fgettext("High"),
fgettext("Very High"),
fgettext("Custom"),
}
local PRESET_DISABLED = 1
local PRESET_CUSTOM = #shadow_levels_labels
-- max distance, texture size, texture_32bit, filters, map color
local shadow_presets = {
[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 function detect_mapping_idx()
if not core.settings:get_bool("enable_dynamic_shadows", false) then
return PRESET_DISABLED
end
local shadow_map_max_distance = tonumber(core.settings:get("shadow_map_max_distance"))
local shadow_map_texture_size = tonumber(core.settings:get("shadow_map_texture_size"))
local shadow_map_texture_32bit = core.settings:get_bool("shadow_map_texture_32bit", false)
local shadow_filters = tonumber(core.settings:get("shadow_filters"))
local shadow_map_color = core.settings:get_bool("shadow_map_color", false)
for i = 2, 6 do
local preset = shadow_presets[i]
if preset[1] == shadow_map_max_distance and
preset[2] == shadow_map_texture_size and
preset[3] == shadow_map_texture_32bit and
preset[4] == shadow_filters and
preset[5] == shadow_map_color then
return i
end
end
return PRESET_CUSTOM
end
local function apply_preset(preset)
if preset then
core.settings:set_bool("enable_dynamic_shadows", true)
core.settings:set("shadow_map_max_distance", preset[1])
core.settings:set("shadow_map_texture_size", preset[2])
core.settings:set_bool("shadow_map_texture_32bit", preset[3])
core.settings:set("shadow_filters", preset[4])
core.settings:set_bool("shadow_map_color", preset[5])
else
core.settings:set_bool("enable_dynamic_shadows", false)
end
end
return {
query_text = "Shadows",
requires = {
opengl = true,
},
get_formspec = function(self, avail_w)
local labels = table.copy(shadow_levels_labels)
local idx = detect_mapping_idx()
-- Remove "custom" if not already selected
if idx ~= PRESET_CUSTOM then
table.remove(labels, PRESET_CUSTOM)
end
local fs =
"label[0,0.2;" .. fgettext("Dynamic shadows") .. "]" ..
"dropdown[0,0.4;3,0.8;dd_shadows;" .. table.concat(labels, ",") .. ";" .. idx .. ";true]" ..
"label[0,1.5;" .. core.colorize("#bbb", fgettext("(The game will need to enable shadows as well)")) .. "]"
return fs, 1.8
end,
on_submit = function(self, fields)
if fields.dd_shadows then
local old_shadow_level_idx = detect_mapping_idx()
local shadow_level_idx = tonumber(fields.dd_shadows)
if shadow_level_idx == nil or shadow_level_idx == old_shadow_level_idx then
return false
end
if shadow_level_idx == PRESET_CUSTOM then
core.settings:set_bool("enable_dynamic_shadows", true)
return true
end
local preset = shadow_presets[shadow_level_idx]
apply_preset(preset)
return true
end
end,
}

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2013 sapier
--
--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.
-- Luanti
-- Copyright (C) 2013 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function prepare_credits(dest, source)

View file

@ -1,20 +1,7 @@
--Luanti
--Copyright (C) 2014 sapier
--Copyright (C) 2018 rubenwardy <rw@rubenwardy.com>
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- Copyright (C) 2018 rubenwardy <rw@rubenwardy.com>
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function get_content_icons(packages_with_updates)

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
local current_game, singleplayer_refresh_gamebar

View file

@ -1,19 +1,6 @@
--Luanti
--Copyright (C) 2014 sapier
--
--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.
-- Luanti
-- Copyright (C) 2014 sapier
-- SPDX-License-Identifier: LGPL-2.1-or-later
local function get_sorted_servers()
local servers = {
@ -129,6 +116,7 @@ local function get_formspec(tabview, name, tabdata)
local retval =
-- Search
"field[0.25,0.25;7,0.75;te_search;;" .. core.formspec_escape(tabdata.search_for) .. "]" ..
"tooltip[te_search;" .. fgettext("Possible filters\ngame:<name>\nmod:<name>\nplayer:<name>") .. "]" ..
"field_enter_after_edit[te_search;true]" ..
"container[7.25,0.25]" ..
"image_button[0,0;0.75,0.75;" .. core.formspec_escape(defaulttexturedir .. "search.png") .. ";btn_mp_search;]" ..
@ -177,6 +165,26 @@ local function get_formspec(tabview, name, tabdata)
core.formspec_escape(gamedata.serverdescription) .. "]"
end
-- Mods button
local mods = selected_server.mods
if mods and #mods > 0 then
local tooltip = ""
if selected_server.gameid then
tooltip = fgettext("Game: $1", selected_server.gameid) .. "\n"
end
tooltip = tooltip .. fgettext("Number of mods: $1", #mods)
retval = retval ..
"tooltip[btn_view_mods;" .. tooltip .. "]" ..
"style[btn_view_mods;padding=6]" ..
"image_button[4,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir ..
"server_view_mods.png") .. ";btn_view_mods;]"
else
retval = retval .. "image[4.1,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir ..
"server_view_mods_unavailable.png") .. "]"
end
-- Clients list button
local clients_list = selected_server.clients_list
local can_view_clients_list = clients_list and #clients_list > 0
if can_view_clients_list then
@ -186,23 +194,31 @@ local function get_formspec(tabview, name, tabdata)
local max_clients = 5
if #clients_list > max_clients then
retval = retval .. "tooltip[btn_view_clients;" ..
fgettext("Clients:\n$1", table.concat(clients_list, "\n", 1, max_clients)) .. "\n..." .. "]"
fgettext("Players:\n$1", table.concat(clients_list, "\n", 1, max_clients)) .. "\n..." .. "]"
else
retval = retval .. "tooltip[btn_view_clients;" ..
fgettext("Clients:\n$1", table.concat(clients_list, "\n")) .. "]"
fgettext("Players:\n$1", table.concat(clients_list, "\n")) .. "]"
end
retval = retval .. "style[btn_view_clients;padding=6]"
retval = retval .. "image_button[4.5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir ..
"server_view_clients.png") .. ";btn_view_clients;]"
else
retval = retval .. "image[4.6,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir ..
"server_view_clients_unavailable.png") .. "]"
end
-- URL button
if selected_server.url then
retval = retval .. "tooltip[btn_server_url;" .. fgettext("Open server website") .. "]"
retval = retval .. "style[btn_server_url;padding=6]"
retval = retval .. "image_button[" .. (can_view_clients_list and "4" or "4.5") .. ",1.3;0.5,0.5;" ..
retval = retval .. "image_button[3.5,1.3;0.5,0.5;" ..
core.formspec_escape(defaulttexturedir .. "server_url.png") .. ";btn_server_url;]"
else
retval = retval .. "image[3.6,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir ..
"server_url_unavailable.png") .. "]"
end
-- Favorites toggle button
if is_selected_fav() then
retval = retval .. "tooltip[btn_delete_favorite;" .. fgettext("Remove favorite") .. "]"
retval = retval .. "style[btn_delete_favorite;padding=6]"
@ -289,19 +305,109 @@ end
--------------------------------------------------------------------------------
local function search_server_list(input)
local function parse_search_input(input)
if not input:find("%S") then
return -- Return nil if nothing to search for
end
-- Search is not case sensitive
input = input:lower()
local query = {keywords = {}, mods = {}, players = {}}
-- Process quotation enclosed parts
input = input:gsub('(%S?)"([^"]*)"(%S?)', function(before, match, after)
if before == "" and after == "" then -- Also have be separated by spaces
table.insert(query.keywords, match)
return " "
end
return before..'"'..match..'"'..after
end)
-- Separate by space characters and handle special prefixes
-- (words with special prefixes need an exact match and none of them can contain spaces)
for word in input:gmatch("%S+") do
local mod = word:match("^mod:(.*)")
table.insert(query.mods, mod)
local player = word:match("^player:(.*)")
table.insert(query.players, player)
local game = word:match("^game:(.*)")
query.game = query.game or game
if not (mod or player or game) then
table.insert(query.keywords, word)
end
end
return query
end
-- Prepares the server to be used for searching
local function uncapitalize_server(server)
local function table_lower(t)
local r = {}
for i, s in ipairs(t or {}) do
r[i] = s:lower()
end
return r
end
return {
name = (server.name or ""):lower(),
description = (server.description or ""):lower(),
gameid = (server.gameid or ""):lower(),
mods = table_lower(server.mods),
clients_list = table_lower(server.clients_list),
}
end
-- Returns false if the query does not match
-- otherwise returns a number to adjust the sorting priority
local function matches_query(server, query)
-- Search is not case sensitive
server = uncapitalize_server(server)
-- Check if mods found
for _, mod in ipairs(query.mods) do
if table.indexof(server.mods, mod) < 0 then
return false
end
end
-- Check if players found
for _, player in ipairs(query.players) do
if table.indexof(server.clients_list, player) < 0 then
return false
end
end
-- Check if game matches
if query.game and query.game ~= server.gameid then
return false
end
-- Check if keyword found
local name_matches = true
local description_matches = true
for _, keyword in ipairs(query.keywords) do
name_matches = name_matches and server.name:find(keyword, 1, true)
description_matches = description_matches and server.description:find(keyword, 1, true)
end
return name_matches and 50 or description_matches and 0
end
local function search_server_list(input, tabdata)
menudata.search_result = nil
if #serverlistmgr.servers < 2 then
return
end
-- setup the keyword list
local keywords = {}
for word in input:gmatch("%S+") do
table.insert(keywords, word:lower())
end
if #keywords == 0 then
tabdata.pre_search_selection = tabdata.pre_search_selection or find_selected_server()
-- setup the search query
local query = parse_search_input(input)
if not query then
return
end
@ -310,16 +416,9 @@ local function search_server_list(input)
-- Search the serverlist
local search_result = {}
for i, server in ipairs(serverlistmgr.servers) do
local name_matches, description_matches = true, true
for _, keyword in ipairs(keywords) do
name_matches = name_matches and not not
(server.name or ""):lower():find(keyword, 1, true)
description_matches = description_matches and not not
(server.description or ""):lower():find(keyword, 1, true)
end
if name_matches or description_matches then
server.points = #serverlistmgr.servers - i
+ (name_matches and 50 or 0)
local match = matches_query(server, query)
if match then
server.points = #serverlistmgr.servers - i + match
table.insert(search_result, server)
end
end
@ -328,10 +427,32 @@ local function search_server_list(input)
return
end
local current_server = find_selected_server()
table.sort(search_result, function(a, b)
return a.points > b.points
end)
menudata.search_result = search_result
-- Keep current selection if it's in search results
if current_server then
for _, server in ipairs(search_result) do
if server.address == current_server.address and
server.port == current_server.port then
return
end
end
end
-- Find first compatible server (favorite or public)
for _, server in ipairs(search_result) do
if is_server_protocol_compat(server.proto_min, server.proto_max) then
set_selected_server(server)
return
end
end
-- If no compatible server found, clear selection
set_selected_server(nil)
end
local function main_button_handler(tabview, fields, name, tabdata)
@ -371,6 +492,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
end
if event.type == "CHG" then
set_selected_server(server)
tabdata.pre_search_selection = nil
return true
end
end
@ -384,11 +506,9 @@ local function main_button_handler(tabview, fields, name, tabdata)
if fields.btn_delete_favorite then
local idx = core.get_table_index("servers")
if not idx then return end
local server = tabdata.lookup[idx]
if not server then return end
serverlistmgr.delete_favorite(server)
set_selected_server(server)
serverlistmgr.delete_favorite(tabdata.lookup[idx])
set_selected_server(tabdata.lookup[idx+1])
return true
end
@ -405,20 +525,27 @@ local function main_button_handler(tabview, fields, name, tabdata)
return true
end
if fields.btn_view_mods then
local dlg = create_server_list_mods_dialog(find_selected_server())
dlg:set_parent(tabview)
tabview:hide()
dlg:show()
return true
end
if fields.btn_mp_clear then
tabdata.search_for = ""
menudata.search_result = nil
if tabdata.pre_search_selection then
set_selected_server(tabdata.pre_search_selection)
tabdata.pre_search_selection = nil
end
return true
end
if fields.btn_mp_search or fields.key_enter_field == "te_search" then
tabdata.search_for = fields.te_search
search_server_list(fields.te_search:lower())
if menudata.search_result then
-- Note: This clears the selection if there are no results
set_selected_server(menudata.search_result[1])
end
search_server_list(fields.te_search, tabdata)
return true
end