diff --git a/builtin/mainmenu/content/contentdb.lua b/builtin/mainmenu/content/contentdb.lua deleted file mode 100644 index e0479cb4c265..000000000000 --- a/builtin/mainmenu/content/contentdb.lua +++ /dev/null @@ -1,583 +0,0 @@ ---Minetest ---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. - -if not core.get_http_api then - return -end - - -contentdb = { - loading = false, - load_ok = false, - load_error = false, - - -- Unordered preserves the original order of the ContentDB API, - -- before the package list is ordered based on installed state. - packages = {}, - packages_full = {}, - packages_full_unordered = {}, - package_by_id = {}, - aliases = {}, - - number_downloading = 0, - download_queue = {}, - - REASON_NEW = "new", - REASON_UPDATE = "update", - REASON_DEPENDENCY = "dependency", -} - - -local function get_download_url(package, reason) - local base_url = core.settings:get("contentdb_url") - local ret = base_url .. ("/packages/%s/releases/%d/download/"):format( - package.url_part, package.release) - if reason then - ret = ret .. "?reason=" .. reason - end - return ret -end - - -local function download_and_extract(param) - local package = param.package - - local filename = core.get_temp_path(true) - if filename == "" or not core.download_file(param.url, filename) then - core.log("error", "Downloading " .. dump(param.url) .. " failed") - return { - msg = fgettext_ne("Failed to download \"$1\"", package.title) - } - end - - local tempfolder = core.get_temp_path() - if tempfolder ~= "" and not core.extract_zip(filename, tempfolder) then - tempfolder = "" - end - os.remove(filename) - if tempfolder == "" then - return { - msg = fgettext_ne("Failed to extract \"$1\" " .. - "(insufficient disk space, unsupported file type or broken archive)", - package.title), - } - end - - return { - path = tempfolder - } -end - - -local function start_install(package, reason) - local params = { - package = package, - url = get_download_url(package, reason), - } - - contentdb.number_downloading = contentdb.number_downloading + 1 - - local function callback(result) - if result.msg then - gamedata.errormessage = result.msg - else - local path, msg = pkgmgr.install_dir(package.type, result.path, package.name, package.path) - core.delete_dir(result.path) - if not path then - gamedata.errormessage = fgettext_ne("Error installing \"$1\": $2", package.title, msg) - else - core.log("action", "Installed package to " .. path) - - local conf_path - local name_is_title = false - if package.type == "mod" then - local actual_type = pkgmgr.get_folder_type(path) - if actual_type.type == "modpack" then - conf_path = path .. DIR_DELIM .. "modpack.conf" - else - conf_path = path .. DIR_DELIM .. "mod.conf" - end - elseif package.type == "game" then - conf_path = path .. DIR_DELIM .. "game.conf" - name_is_title = true - elseif package.type == "txp" then - conf_path = path .. DIR_DELIM .. "texture_pack.conf" - end - - if conf_path then - local conf = Settings(conf_path) - if not conf:get("title") then - conf:set("title", package.title) - end - if not name_is_title then - conf:set("name", package.name) - end - if not conf:get("description") then - conf:set("description", package.short_description) - end - conf:set("author", package.author) - conf:set("release", package.release) - conf:write() - end - - pkgmgr.reload_by_type(package.type) - end - end - - package.downloading = false - - contentdb.number_downloading = contentdb.number_downloading - 1 - - local next = contentdb.download_queue[1] - if next then - table.remove(contentdb.download_queue, 1) - - start_install(next.package, next.reason) - end - ui.update() - end - - package.queued = false - package.downloading = true - - if not core.handle_async(download_and_extract, params, callback) then - core.log("error", "ERROR: async event failed") - gamedata.errormessage = fgettext_ne("Failed to download $1", package.name) - return - end -end - - -function contentdb.queue_download(package, reason) - if package.queued or package.downloading then - return - end - - local max_concurrent_downloads = tonumber(core.settings:get("contentdb_max_concurrent_downloads")) - if contentdb.number_downloading < math.max(max_concurrent_downloads, 1) then - start_install(package, reason) - else - table.insert(contentdb.download_queue, { package = package, reason = reason }) - package.queued = true - end -end - - -function contentdb.get_package_by_id(id) - return contentdb.package_by_id[id] -end - - --- Create a coroutine from `fn` and provide results to `callback` when complete (dead). --- Returns a resumer function. -local function make_callback_coroutine(fn, callback) - local co = coroutine.create(fn) - - local function resumer(...) - local ok, result = coroutine.resume(co, ...) - - if not ok then - error(result) - elseif coroutine.status(co) == "dead" then - callback(result) - end - end - - return resumer -end - - -local function get_raw_dependencies_async(package) - local url_fmt = "/api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s" - local version = core.get_version() - local base_url = core.settings:get("contentdb_url") - local url = base_url .. url_fmt:format(package.url_part, core.get_max_supp_proto(), core.urlencode(version.string)) - - local http = core.get_http_api() - local response = http.fetch_sync({ url = url }) - if not response.succeeded then - return nil - end - return core.parse_json(response.data) or {} -end - - -local function get_raw_dependencies_co(package, resumer) - if package.type ~= "mod" then - return {} - end - if package.raw_deps then - return package.raw_deps - end - - core.handle_async(get_raw_dependencies_async, package, resumer) - local data = coroutine.yield() - if not data then - return nil - end - - for id, raw_deps in pairs(data) do - local package2 = contentdb.package_by_id[id:lower()] - if package2 and not package2.raw_deps then - package2.raw_deps = raw_deps - - for _, dep in pairs(raw_deps) do - local packages = {} - for i=1, #dep.packages do - packages[#packages + 1] = contentdb.package_by_id[dep.packages[i]:lower()] - end - dep.packages = packages - end - end - end - - return package.raw_deps -end - - -local function has_hard_deps_co(package, resumer) - local raw_deps = get_raw_dependencies_co(package, resumer) - if not raw_deps then - return nil - end - - for i=1, #raw_deps do - if not raw_deps[i].is_optional then - return true - end - end - - return false -end - - -function contentdb.has_hard_deps(package, callback) - local resumer = make_callback_coroutine(has_hard_deps_co, callback) - resumer(package, resumer) -end - - --- Recursively resolve dependencies, given the installed mods -local function resolve_dependencies_2_co(raw_deps, installed_mods, out, resumer) - local function resolve_dep(dep) - -- Check whether it's already installed - if installed_mods[dep.name] then - return { - is_optional = dep.is_optional, - name = dep.name, - installed = true, - } - end - - -- Find exact name matches - local fallback - for _, package in pairs(dep.packages) do - if package.type ~= "game" then - if package.name == dep.name then - return { - is_optional = dep.is_optional, - name = dep.name, - installed = false, - package = package, - } - elseif not fallback then - fallback = package - end - end - end - - -- Otherwise, find the first mod that fulfills it - if fallback then - return { - is_optional = dep.is_optional, - name = dep.name, - installed = false, - package = fallback, - } - end - - return { - is_optional = dep.is_optional, - name = dep.name, - installed = false, - } - end - - for _, dep in pairs(raw_deps) do - if not dep.is_optional and not out[dep.name] then - local result = resolve_dep(dep) - out[dep.name] = result - if result and result.package and not result.installed then - local raw_deps2 = get_raw_dependencies_co(result.package, resumer) - if raw_deps2 then - resolve_dependencies_2_co(raw_deps2, installed_mods, out, resumer) - end - end - end - end - - return true -end - - -local function resolve_dependencies_co(package, game, resumer) - assert(game) - - local raw_deps = get_raw_dependencies_co(package, resumer) - local installed_mods = {} - - local mods = {} - pkgmgr.get_game_mods(game, mods) - for _, mod in pairs(mods) do - installed_mods[mod.name] = true - end - - for _, mod in pairs(pkgmgr.global_mods:get_list()) do - installed_mods[mod.name] = true - end - - local out = {} - if not resolve_dependencies_2_co(raw_deps, installed_mods, out, resumer) then - return nil - end - - local retval = {} - for _, dep in pairs(out) do - retval[#retval + 1] = dep - end - - table.sort(retval, function(a, b) - return a.name < b.name - end) - - return retval -end - - --- Resolve dependencies for a package, calls the recursive version. -function contentdb.resolve_dependencies(package, game, callback) - local resumer = make_callback_coroutine(resolve_dependencies_co, callback) - resumer(package, game, resumer) -end - - -local function fetch_pkgs(params) - local version = core.get_version() - local base_url = core.settings:get("contentdb_url") - 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 - url = url .. "&hide=" .. core.urlencode(item) - 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, ", ") - }, - }) - if not response.succeeded then - return - end - - local packages = core.parse_json(response.data) - if not packages or #packages == 0 then - return - end - local aliases = {} - - for _, package in pairs(packages) do - local name_len = #package.name - -- This must match what contentdb.update_paths() does! - package.id = package.author:lower() .. "/" - if package.type == "game" and name_len > 5 and package.name:sub(name_len - 4) == "_game" then - package.id = package.id .. package.name:sub(1, name_len - 5) - else - package.id = package.id .. package.name - end - - package.url_part = core.urlencode(package.author) .. "/" .. core.urlencode(package.name) - - if package.aliases then - for _, alias in ipairs(package.aliases) do - -- We currently don't support name changing - local suffix = "/" .. package.name - if alias:sub(-#suffix) == suffix then - aliases[alias:lower()] = package.id - end - end - end - end - - return { packages = packages, aliases = aliases } -end - - -function contentdb.fetch_pkgs(callback) - contentdb.loading = true - core.handle_async(fetch_pkgs, nil, function(result) - if result then - contentdb.load_ok = true - contentdb.load_error = false - contentdb.packages = result.packages - contentdb.packages_full = result.packages - contentdb.packages_full_unordered = result.packages - contentdb.aliases = result.aliases - - for _, package in ipairs(result.packages) do - contentdb.package_by_id[package.id] = package - end - else - contentdb.load_error = true - end - - contentdb.loading = false - callback(result) - end) -end - - -function contentdb.update_paths() - pkgmgr.load_all() - - local mod_hash = {} - for _, mod in pairs(pkgmgr.global_mods:get_list()) do - local cdb_id = pkgmgr.get_contentdb_id(mod) - if cdb_id then - mod_hash[contentdb.aliases[cdb_id] or cdb_id] = mod - end - end - - local game_hash = {} - for _, game in pairs(pkgmgr.games) do - local cdb_id = pkgmgr.get_contentdb_id(game) - if cdb_id then - game_hash[contentdb.aliases[cdb_id] or cdb_id] = game - end - end - - local txp_hash = {} - for _, txp in pairs(pkgmgr.texture_packs) do - local cdb_id = pkgmgr.get_contentdb_id(txp) - if cdb_id then - txp_hash[contentdb.aliases[cdb_id] or cdb_id] = txp - end - end - - for _, package in pairs(contentdb.packages_full) do - local content - if package.type == "mod" then - content = mod_hash[package.id] - elseif package.type == "game" then - content = game_hash[package.id] - elseif package.type == "txp" then - content = txp_hash[package.id] - end - - if content then - package.path = content.path - package.installed_release = content.release or 0 - else - package.path = nil - package.installed_release = nil - end - end -end - - -function contentdb.sort_packages() - local ret = {} - - -- Add installed content - for _, pkg in ipairs(contentdb.packages_full_unordered) do - if pkg.path then - ret[#ret + 1] = pkg - end - end - - -- Sort installed content first by "is there an update available?", then by title - table.sort(ret, function(a, b) - local a_updatable = a.installed_release < a.release - local b_updatable = b.installed_release < b.release - if a_updatable and not b_updatable then - return true - elseif b_updatable and not a_updatable then - return false - end - - return a.title < b.title - end) - - -- Add uninstalled content - for _, pkg in ipairs(contentdb.packages_full_unordered) do - if not pkg.path then - ret[#ret + 1] = pkg - end - end - - contentdb.packages_full = ret -end - - -function contentdb.filter_packages(query, by_type) - if query == "" and by_type == nil then - contentdb.packages = contentdb.packages_full - return - end - - local keywords = {} - for word in query:lower():gmatch("%S+") do - table.insert(keywords, word) - end - - local function matches_keywords(package) - for k = 1, #keywords do - local keyword = keywords[k] - - if string.find(package.name:lower(), keyword, 1, true) or - string.find(package.title:lower(), keyword, 1, true) or - string.find(package.author:lower(), keyword, 1, true) or - string.find(package.short_description:lower(), keyword, 1, true) then - return true - end - end - - return false - end - - contentdb.packages = {} - for _, package in pairs(contentdb.packages_full) do - if (query == "" or matches_keywords(package)) and - (by_type == nil or package.type == by_type) then - contentdb.packages[#contentdb.packages + 1] = package - end - end -end diff --git a/builtin/mainmenu/content/dlg_contentdb.lua b/builtin/mainmenu/content/dlg_contentdb.lua deleted file mode 100644 index 84ef96800cba..000000000000 --- a/builtin/mainmenu/content/dlg_contentdb.lua +++ /dev/null @@ -1,511 +0,0 @@ ---Minetest ---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. - -if not core.get_http_api then - function create_contentdb_dlg() - return messagebox("contentdb", - fgettext("ContentDB is not available when Minetest was compiled without cURL")) - end - return -end - --- Filter -local search_string = "" -local cur_page = 1 -local num_per_page = 5 -local filter_type = 1 -local filter_types_titles = { - fgettext("All packages"), - fgettext("Games"), - fgettext("Mods"), - fgettext("Texture packs"), -} - --- Automatic package installation -local auto_install_spec = nil - -local filter_types_type = { - nil, - "game", - "mod", - "txp", -} - - -local function install_or_update_package(this, package) - local install_parent - if package.type == "mod" then - install_parent = core.get_modpath() - elseif package.type == "game" then - install_parent = core.get_gamepath() - elseif package.type == "txp" then - install_parent = core.get_texturepath() - else - error("Unknown package type: " .. package.type) - end - - if package.queued or package.downloading then - return - end - - local function on_confirm() - local dlg = create_install_dialog(package) - dlg:set_parent(this) - this:hide() - dlg:show() - - dlg:load_deps() - end - - if package.type == "mod" and #pkgmgr.games == 0 then - local dlg = messagebox("install_game", - fgettext("You need to install a game before you can install a mod")) - dlg:set_parent(this) - this:hide() - dlg:show() - elseif not package.path and core.is_dir(install_parent .. DIR_DELIM .. package.name) then - local dlg = create_confirm_overwrite(package, on_confirm) - dlg:set_parent(this) - this:hide() - dlg:show() - else - on_confirm() - end -end - - --- Resolves the package specification stored in auto_install_spec into an actual package. --- May only be called after the package list has been loaded successfully. -local function resolve_auto_install_spec() - assert(contentdb.load_ok) - - if not auto_install_spec then - return nil - end - - local spec = contentdb.aliases[auto_install_spec] or auto_install_spec - local resolved = nil - - for _, pkg in ipairs(contentdb.packages_full_unordered) do - if pkg.id == spec then - resolved = pkg - break - end - end - - if not resolved then - gamedata.errormessage = fgettext_ne("The package $1 was not found.", auto_install_spec) - ui.update() - - auto_install_spec = nil - end - - return resolved -end - - --- Installs the package specified by auto_install_spec. --- Only does something if: --- a. The package list has been loaded successfully. --- b. The ContentDB dialog is currently visible. -local function do_auto_install() - if not contentdb.load_ok then - return - end - - local pkg = resolve_auto_install_spec() - if not pkg then - return - end - - local contentdb_dlg = ui.find_by_name("contentdb") - if not contentdb_dlg or contentdb_dlg.hidden then - return - end - - install_or_update_package(contentdb_dlg, pkg) - auto_install_spec = nil -end - - -local function sort_and_filter_pkgs() - contentdb.update_paths() - contentdb.sort_packages() - contentdb.filter_packages(search_string, filter_types_type[filter_type]) - - local auto_install_pkg = resolve_auto_install_spec() - if auto_install_pkg then - local idx = table.indexof(contentdb.packages, auto_install_pkg) - if idx ~= -1 then - table.remove(contentdb.packages, idx) - table.insert(contentdb.packages, 1, auto_install_pkg) - end - end -end - - -local function load() - if contentdb.load_ok then - sort_and_filter_pkgs() - return - end - if contentdb.loading then - return - end - contentdb.fetch_pkgs(function(result) - if result then - sort_and_filter_pkgs() - do_auto_install() - end - ui.update() - end) -end - - -local function get_info_formspec(text) - local H = 9.5 - return table.concat({ - "formspec_version[6]", - "size[15.75,9.5]", - core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]", - - "label[4,4.35;", text, "]", - "container[0,", H - 0.8 - 0.375, "]", - "button[0.375,0;5,0.8;back;", fgettext("Back to Main Menu"), "]", - "container_end[]", - }) -end - - -local function get_formspec(dlgdata) - if contentdb.loading then - return get_info_formspec(fgettext("Loading...")) - end - if contentdb.load_error then - return get_info_formspec(fgettext("No packages could be retrieved")) - end - assert(contentdb.load_ok) - - contentdb.update_paths() - - dlgdata.pagemax = math.max(math.ceil(#contentdb.packages / num_per_page), 1) - if cur_page > dlgdata.pagemax then - cur_page = 1 - end - - local W = 15.75 - local H = 9.5 - local formspec = { - "formspec_version[6]", - "size[15.75,9.5]", - core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]", - - "style[status,downloading,queued;border=false]", - - "container[0.375,0.375]", - "field[0,0;7.225,0.8;search_string;;", core.formspec_escape(search_string), "]", - "field_enter_after_edit[search_string;true]", - "image_button[7.3,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]", - "image_button[8.125,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";clear;]", - "dropdown[9.175,0;2.7875,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]", - "container_end[]", - - -- Page nav buttons - "container[0,", H - 0.8 - 0.375, "]", - "button[0.375,0;5,0.8;back;", fgettext("Back to Main Menu"), "]", - - "container[", W - 0.375 - 0.8*4 - 2, ",0]", - "image_button[0,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "start_icon.png;pstart;]", - "image_button[0.8,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "prev_icon.png;pback;]", - "style[pagenum;border=false]", - "button[1.6,0;2,0.8;pagenum;", tonumber(cur_page), " / ", tonumber(dlgdata.pagemax), "]", - "image_button[3.6,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "next_icon.png;pnext;]", - "image_button[4.4,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "end_icon.png;pend;]", - "container_end[]", - - "container_end[]", - } - - if contentdb.number_downloading > 0 then - formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;downloading;" - if #contentdb.download_queue > 0 then - formspec[#formspec + 1] = fgettext("$1 downloading,\n$2 queued", - contentdb.number_downloading, #contentdb.download_queue) - else - formspec[#formspec + 1] = fgettext("$1 downloading...", contentdb.number_downloading) - end - formspec[#formspec + 1] = "]" - else - local num_avail_updates = 0 - for i=1, #contentdb.packages_full do - local package = contentdb.packages_full[i] - if package.path and package.installed_release < package.release and - not (package.downloading or package.queued) then - num_avail_updates = num_avail_updates + 1 - end - end - - if num_avail_updates == 0 then - formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;status;" - formspec[#formspec + 1] = fgettext("No updates") - formspec[#formspec + 1] = "]" - else - formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;update_all;" - formspec[#formspec + 1] = fgettext("Update All [$1]", num_avail_updates) - formspec[#formspec + 1] = "]" - end - end - - if #contentdb.packages == 0 then - formspec[#formspec + 1] = "label[4,4.75;" - formspec[#formspec + 1] = fgettext("No results") - formspec[#formspec + 1] = "]" - end - - -- download/queued tooltips always have the same message - local tooltip_colors = ";#dff6f5;#302c2e]" - formspec[#formspec + 1] = "tooltip[downloading;" .. fgettext("Downloading...") .. tooltip_colors - formspec[#formspec + 1] = "tooltip[queued;" .. fgettext("Queued") .. tooltip_colors - - 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 container_y = (i - start_idx) * 1.375 + (2*0.375 + 0.8) - formspec[#formspec + 1] = "container[0.375," - formspec[#formspec + 1] = container_y - formspec[#formspec + 1] = "]" - - -- image - formspec[#formspec + 1] = "image[0,0;1.5,1;" - formspec[#formspec + 1] = core.formspec_escape(get_screenshot(package)) - formspec[#formspec + 1] = "]" - - -- title - formspec[#formspec + 1] = "label[1.875,0.1;" - formspec[#formspec + 1] = core.formspec_escape( - core.colorize(mt_color_green, package.title) .. - core.colorize("#BFBFBF", " by " .. package.author)) - formspec[#formspec + 1] = "]" - - -- buttons - local description_width = W - 2.625 - 2 * 0.7 - 2 * 0.15 - - local second_base = "image_button[-1.55,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir) - local third_base = "image_button[-2.4,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir) - formspec[#formspec + 1] = "container[" - formspec[#formspec + 1] = W - 0.375*2 - formspec[#formspec + 1] = ",0.1]" - - if package.downloading then - formspec[#formspec + 1] = "animated_image[-1.7,-0.15;1,1;downloading;" - formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir) - formspec[#formspec + 1] = "cdb_downloading.png;3;400;]" - elseif package.queued then - formspec[#formspec + 1] = second_base - formspec[#formspec + 1] = "cdb_queued.png;queued;]" - elseif not package.path then - local elem_name = "install_" .. i .. ";" - formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#71aa34]" - formspec[#formspec + 1] = second_base .. "cdb_add.png;" .. elem_name .. "]" - formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Install") .. tooltip_colors - else - if package.installed_release < package.release then - -- The install_ action also handles updating - local elem_name = "install_" .. i .. ";" - formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#28ccdf]" - formspec[#formspec + 1] = third_base .. "cdb_update.png;" .. elem_name .. "]" - formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Update") .. tooltip_colors - - description_width = description_width - 0.7 - 0.15 - end - - local elem_name = "uninstall_" .. i .. ";" - formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#a93b3b]" - formspec[#formspec + 1] = second_base .. "cdb_clear.png;" .. elem_name .. "]" - formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Uninstall") .. tooltip_colors - end - - local web_elem_name = "view_" .. i .. ";" - formspec[#formspec + 1] = "image_button[-0.7,0;0.7,0.7;" .. - core.formspec_escape(defaulttexturedir) .. "cdb_viewonline.png;" .. web_elem_name .. "]" - formspec[#formspec + 1] = "tooltip[" .. web_elem_name .. - fgettext("View more information in a web browser") .. tooltip_colors - formspec[#formspec + 1] = "container_end[]" - - -- description - formspec[#formspec + 1] = "textarea[1.855,0.3;" - formspec[#formspec + 1] = tostring(description_width) - formspec[#formspec + 1] = ",0.8;;;" - formspec[#formspec + 1] = core.formspec_escape(package.short_description) - formspec[#formspec + 1] = "]" - - formspec[#formspec + 1] = "container_end[]" - end - - return table.concat(formspec) -end - - -local function handle_submit(this, fields) - if fields.search or fields.key_enter_field == "search_string" then - search_string = fields.search_string:trim() - cur_page = 1 - contentdb.filter_packages(search_string, filter_types_type[filter_type]) - return true - end - - if fields.clear then - search_string = "" - cur_page = 1 - contentdb.filter_packages("", filter_types_type[filter_type]) - return true - end - - if fields.back then - this:delete() - return true - end - - if fields.pstart then - cur_page = 1 - return true - end - - if fields.pend then - cur_page = this.data.pagemax - return true - end - - if fields.pnext then - cur_page = cur_page + 1 - if cur_page > this.data.pagemax then - cur_page = 1 - end - return true - end - - if fields.pback then - if cur_page == 1 then - cur_page = this.data.pagemax - else - cur_page = cur_page - 1 - end - return true - end - - if fields.type then - local new_type = table.indexof(filter_types_titles, fields.type) - if new_type ~= filter_type then - filter_type = new_type - cur_page = 1 - contentdb.filter_packages(search_string, filter_types_type[filter_type]) - return true - end - end - - if fields.update_all then - for i=1, #contentdb.packages_full do - local package = contentdb.packages_full[i] - if package.path and package.installed_release < package.release and - not (package.downloading or package.queued) then - contentdb.queue_download(package, contentdb.REASON_UPDATE) - end - end - return true - end - - local start_idx = (cur_page - 1) * num_per_page + 1 - assert(start_idx ~= nil) - for i=start_idx, math.min(#contentdb.packages, start_idx+num_per_page-1) do - local package = contentdb.packages[i] - assert(package) - - if fields["install_" .. i] then - install_or_update_package(this, package) - return true - end - - if fields["uninstall_" .. i] then - local dlg = create_delete_content_dlg(package) - dlg:set_parent(this) - this:hide() - dlg:show() - return true - end - - if fields["view_" .. i] then - local url = ("%s/packages/%s?protocol_version=%d"):format( - core.settings:get("contentdb_url"), package.url_part, - core.get_max_supp_proto()) - core.open_url(url) - return true - end - end - - return false -end - - -local function handle_events(event) - if event == "DialogShow" then - -- On touchscreen, don't show the "MINETEST" header behind the dialog. - mm_game_theme.set_engine(core.settings:get_bool("enable_touch")) - - -- If ContentDB is already loaded, auto-install packages here. - do_auto_install() - - return true - end - - return false -end - - ---- Creates a ContentDB dialog. ---- ---- @param type string | nil ---- Sets initial package filter. "game", "mod", "txp" or nil (no filter). ---- @param install_spec table | nil ---- ContentDB ID of package as returned by pkgmgr.get_contentdb_id(). ---- Sets package to install or update automatically. -function create_contentdb_dlg(type, install_spec) - search_string = "" - cur_page = 1 - if type then - -- table.indexof does not work on tables that contain `nil` - for i, v in pairs(filter_types_type) do - if v == type then - filter_type = i - break - end - end - else - filter_type = 1 - end - - -- Keep the old auto_install_spec if the caller doesn't specify one. - if install_spec then - auto_install_spec = install_spec - end - - load() - - return dialog_create("contentdb", - get_formspec, - handle_submit, - handle_events) -end diff --git a/builtin/mainmenu/content/dlg_install.lua b/builtin/mainmenu/content/dlg_install.lua deleted file mode 100644 index 0549e23be585..000000000000 --- a/builtin/mainmenu/content/dlg_install.lua +++ /dev/null @@ -1,246 +0,0 @@ ---Minetest ---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. - -local function is_still_visible(dlg) - local this = ui.find_by_name("install_dialog") - return this == dlg and not dlg.hidden -end - - -local function get_loading_formspec() - local ENABLE_TOUCH = core.settings:get_bool("enable_touch") - local w = ENABLE_TOUCH and 14 or 7 - - local formspec = { - "formspec_version[3]", - "size[", w, ",9.05]", - ENABLE_TOUCH and "padding[0.01,0.01]" or "position[0.5,0.55]", - "label[3,4.525;", fgettext("Loading..."), "]", - } - return table.concat(formspec) -end - - -local function get_formspec(data) - if not data.has_hard_deps_ready then - return get_loading_formspec() - end - - local selected_game, selected_game_idx = pkgmgr.find_by_gameid(core.settings:get("menu_last_game")) - if not selected_game_idx then - selected_game_idx = 1 - selected_game = pkgmgr.games[1] - end - - local game_list = {} - for i, game in ipairs(pkgmgr.games) do - game_list[i] = core.formspec_escape(game.title) - end - - if not data.deps_ready[selected_game_idx] and - not data.deps_loading[selected_game_idx] then - data.deps_loading[selected_game_idx] = true - - contentdb.resolve_dependencies(data.package, selected_game, function(deps) - if not is_still_visible(data.dlg) then - return - end - data.deps_ready[selected_game_idx] = deps - ui.update() - end) - end - - -- The value of `data.deps_ready[selected_game_idx]` may have changed - -- since the last if statement since `contentdb.resolve_dependencies` - -- calls the callback immediately if the dependencies are already cached. - if not data.deps_ready[selected_game_idx] then - return get_loading_formspec() - end - - local package = data.package - local will_install_deps = data.will_install_deps - - local deps_to_install = 0 - local deps_not_found = 0 - - data.deps_chosen = data.deps_ready[selected_game_idx] - local formatted_deps = {} - for _, dep in pairs(data.deps_chosen) do - formatted_deps[#formatted_deps + 1] = "#fff" - formatted_deps[#formatted_deps + 1] = core.formspec_escape(dep.name) - if dep.installed then - formatted_deps[#formatted_deps + 1] = "#ccf" - formatted_deps[#formatted_deps + 1] = fgettext("Already installed") - elseif dep.package then - formatted_deps[#formatted_deps + 1] = "#cfc" - formatted_deps[#formatted_deps + 1] = fgettext("$1 by $2", dep.package.title, dep.package.author) - deps_to_install = deps_to_install + 1 - else - formatted_deps[#formatted_deps + 1] = "#f00" - formatted_deps[#formatted_deps + 1] = fgettext("Not found") - deps_not_found = deps_not_found + 1 - end - end - - local message_bg = "#3333" - local message - if will_install_deps then - message = fgettext("$1 and $2 dependencies will be installed.", package.title, deps_to_install) - else - message = fgettext("$1 will be installed, and $2 dependencies will be skipped.", package.title, deps_to_install) - end - if deps_not_found > 0 then - message = fgettext("$1 required dependencies could not be found.", deps_not_found) .. - " " .. fgettext("Please check that the base game is correct.", deps_not_found) .. - "\n" .. message - message_bg = mt_color_orange - end - - local ENABLE_TOUCH = core.settings:get_bool("enable_touch") - - local w = ENABLE_TOUCH and 14 or 7 - local padded_w = w - 2*0.375 - local dropdown_w = ENABLE_TOUCH and 10.2 or 4.25 - local button_w = (padded_w - 0.25) / 3 - local button_pad = button_w / 2 - - local formspec = { - "formspec_version[3]", - "size[", w, ",9.05]", - ENABLE_TOUCH and "padding[0.01,0.01]" or "position[0.5,0.55]", - "style[title;border=false]", - "box[0,0;", w, ",0.8;#3333]", - "button[0,0;", w, ",0.8;title;", fgettext("Install $1", package.title) , "]", - - "container[0.375,1]", - - "label[0,0.4;", fgettext("Base Game:"), "]", - "dropdown[", padded_w - dropdown_w, ",0;", dropdown_w, ",0.8;selected_game;", - table.concat(game_list, ","), ";", selected_game_idx, "]", - - "label[0,1.1;", fgettext("Dependencies:"), "]", - - "tablecolumns[color;text;color;text]", - "table[0,1.4;", padded_w, ",3;packages;", table.concat(formatted_deps, ","), "]", - - "container_end[]", - - "checkbox[0.375,5.7;will_install_deps;", - fgettext("Install missing dependencies"), ";", - will_install_deps and "true" or "false", "]", - - "box[0,6;", w, ",1.8;", message_bg, "]", - "textarea[0.375,6.1;", padded_w, ",1.6;;;", message, "]", - - "container[", 0.375 + button_pad, ",8.05]", - "button[0,0;", button_w, ",0.8;install_all;", fgettext("Install"), "]", - "button[", 0.25 + button_w, ",0;", button_w, ",0.8;cancel;", fgettext("Cancel"), "]", - "container_end[]", - } - - return table.concat(formspec) -end - - -local function handle_submit(this, fields) - local data = this.data - if fields.cancel then - this:delete() - return true - end - - if fields.will_install_deps ~= nil then - data.will_install_deps = core.is_yes(fields.will_install_deps) - return true - end - - if fields.install_all then - contentdb.queue_download(data.package, contentdb.REASON_NEW) - - if data.will_install_deps then - for _, dep in pairs(data.deps_chosen) do - if not dep.is_optional and not dep.installed and dep.package then - contentdb.queue_download(dep.package, contentdb.REASON_DEPENDENCY) - end - end - end - - this:delete() - return true - end - - if fields.selected_game then - for _, game in pairs(pkgmgr.games) do - if game.title == fields.selected_game then - core.settings:set("menu_last_game", game.id) - break - end - end - return true - end - - return false -end - - -local function load_deps(dlg) - local package = dlg.data.package - - contentdb.has_hard_deps(package, function(result) - if not is_still_visible(dlg) then - return - end - - if result == nil then - local parent = dlg.parent - dlg:delete() - local dlg2 = messagebox("error_checking_deps", - fgettext("Error getting dependencies for package $1", package.url_part)) - dlg2:set_parent(parent) - parent:hide() - dlg2:show() - elseif result == false then - contentdb.queue_download(package, package.path and contentdb.REASON_UPDATE or contentdb.REASON_NEW) - dlg:delete() - else - assert(result == true) - dlg.data.has_hard_deps_ready = true - end - ui.update() - end) -end - - -function create_install_dialog(package) - local dlg = dialog_create("install_dialog", get_formspec, handle_submit, nil) - dlg.data.deps_chosen = nil - dlg.data.package = package - dlg.data.will_install_deps = true - - dlg.data.has_hard_deps_ready = false - dlg.data.deps_ready = {} - dlg.data.deps_loading = {} - - dlg.load_deps = load_deps - - -- `get_formspec` needs to access `dlg` to check whether it's still open. - -- It doesn't suffice to check that any "install_dialog" instance is open - -- via `ui.find_by_name`, it's necessary to check for this exact instance. - dlg.data.dlg = dlg - - return dlg -end diff --git a/builtin/mainmenu/content/dlg_overwrite.lua b/builtin/mainmenu/content/dlg_overwrite.lua deleted file mode 100644 index 7967cb48ce1b..000000000000 --- a/builtin/mainmenu/content/dlg_overwrite.lua +++ /dev/null @@ -1,53 +0,0 @@ ---Minetest ---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. - -function get_formspec(data) - local package = data.package - - return confirmation_formspec( - fgettext("\"$1\" already exists. Would you like to overwrite it?", package.name), - 'install', fgettext("Overwrite"), - 'cancel', fgettext("Cancel")) -end - - -local function handle_submit(this, fields) - local data = this.data - if fields.cancel then - this:delete() - return true - end - - if fields.install then - this:delete() - data.callback() - return true - end - - return false -end - - -function create_confirm_overwrite(package, callback) - assert(type(package) == "table") - assert(type(callback) == "function") - - local dlg = dialog_create("data", get_formspec, handle_submit, nil) - dlg.data.package = package - dlg.data.callback = callback - return dlg -end diff --git a/builtin/mainmenu/content/screenshots.lua b/builtin/mainmenu/content/screenshots.lua deleted file mode 100644 index 98c6658b90f0..000000000000 --- a/builtin/mainmenu/content/screenshots.lua +++ /dev/null @@ -1,78 +0,0 @@ ---Minetest ---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. - - --- Screenshot -local screenshot_dir = core.get_cache_path() .. DIR_DELIM .. "cdb" -assert(core.create_dir(screenshot_dir)) -local screenshot_downloading = {} -local screenshot_downloaded = {} - - -local function get_file_extension(path) - local parts = path:split(".") - return parts[#parts] -end - - -function get_screenshot(package) - if not package.thumbnail then - return defaulttexturedir .. "no_screenshot.png" - elseif screenshot_downloading[package.thumbnail] then - return defaulttexturedir .. "loading_screenshot.png" - end - - -- Get tmp screenshot path - local ext = get_file_extension(package.thumbnail) - local filepath = screenshot_dir .. DIR_DELIM .. - ("%s-%s-%s.%s"):format(package.type, package.author, package.name, ext) - - -- Return if already downloaded - local file = io.open(filepath, "r") - if file then - file:close() - return filepath - end - - -- Show error if we've failed to download before - if screenshot_downloaded[package.thumbnail] then - return defaulttexturedir .. "error_screenshot.png" - end - - -- Download - - local function download_screenshot(params) - return core.download_file(params.url, params.dest) - end - local function callback(success) - screenshot_downloading[package.thumbnail] = nil - screenshot_downloaded[package.thumbnail] = true - if not success then - core.log("warning", "Screenshot download failed for some reason") - end - ui.update() - end - if core.handle_async(download_screenshot, - { dest = filepath, url = package.thumbnail }, callback) then - screenshot_downloading[package.thumbnail] = true - else - core.log("error", "ERROR: async event failed") - return defaulttexturedir .. "error_screenshot.png" - end - - return defaulttexturedir .. "loading_screenshot.png" -end diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 41885e29892a..8c7df4105353 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -1,5 +1,5 @@ --Minetest ---Copyright (C) 2014 sapier +--Copyright (C) 2024 Zughy -- --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 @@ -15,115 +15,60 @@ --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" - local menupath = core.get_mainmenu_path() local basepath = core.get_builtin_path() -defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" .. - DIR_DELIM .. "pack" .. DIR_DELIM - -dofile(basepath .. "common" .. DIR_DELIM .. "filterlist.lua") -dofile(basepath .. "fstk" .. DIR_DELIM .. "buttonbar.lua") -dofile(basepath .. "fstk" .. DIR_DELIM .. "dialog.lua") -dofile(basepath .. "fstk" .. DIR_DELIM .. "tabview.lua") -dofile(basepath .. "fstk" .. DIR_DELIM .. "ui.lua") -dofile(menupath .. DIR_DELIM .. "async_event.lua") -dofile(menupath .. DIR_DELIM .. "common.lua") -dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua") -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(menupath .. DIR_DELIM .. "dlg_create_world.lua") -dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua") -dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua") -dofile(menupath .. DIR_DELIM .. "dlg_register.lua") -dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua") -dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua") -dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua") - -local tabs = { - content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"), - about = dofile(menupath .. DIR_DELIM .. "tab_about.lua"), - local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua"), - play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua") -} + +local basetxtrdir = core.get_texturepath_share() .. DIR_DELIM .. "base" .. + DIR_DELIM +defaulttexturedir = basetxtrdir .. "pack" .. DIR_DELIM +local menutxtrdir = basetxtrdir .. DIR_DELIM .. "menu" .. DIR_DELIM + +dofile(menupath .. DIR_DELIM .. "settings/init.lua") + + -------------------------------------------------------------------------------- -local function main_event_handler(tabview, event) - if event == "MenuQuit" then - core.close() - end - return true -end -------------------------------------------------------------------------------- -local function init_globals() - -- Init gamedata - gamedata.worldindex = 0 - - menudata.worldlist = filterlist.create( - core.get_worlds, - compare_worlds, - -- Unique id comparison function - function(element, uid) - return element.name == uid - end, - -- Filter function - function(element, gameid) - return element.gameid == gameid - end - ) - - menudata.worldlist:add_sort_mechanism("alphabetic", sort_worlds_alphabetic) - menudata.worldlist:set_sortmode("alphabetic") - - mm_game_theme.init() - mm_game_theme.set_engine() -- This is just a fallback. - - -- Create main tabview - local tv_main = tabview_create("maintab", {x = 15.5, y = 7.1}, {x = 0, y = 0}) - - tv_main:set_autosave_tab(true) - tv_main:add(tabs.local_game) - tv_main:add(tabs.play_online) - tv_main:add(tabs.content) - tv_main:add(tabs.about) - - tv_main:set_global_event_handler(main_event_handler) - tv_main:set_fixed_size(false) - - local last_tab = core.settings:get("maintab_LAST") - if last_tab and tv_main.current_tab ~= last_tab then - tv_main:set_tab(last_tab) - end - - tv_main:set_end_button({ - icon = defaulttexturedir .. "settings_btn.png", - label = fgettext("Settings"), - name = "open_settings", - on_click = function(tabview) - local dlg = create_settings_dlg() - dlg:set_parent(tabview) - tabview:hide() - dlg:show() - return true - end, - }) - - ui.set_default("maintab") - tv_main:show() - ui.update() - - check_reinstall_mtg() - check_new_version() + +local function get_random_name() + return RANDOM_NAME1[math.random(#RANDOM_NAME1)] .. RANDOM_NAME2[math.random(#RANDOM_NAME2)] +end + +core.set_clouds(false) +core.set_background("background", menutxtrdir .. "mt_bg.png") + +local fs = { + "formspec_version[6]", + "size[5,11,true]", + "no_prepend[]", + "style_type[image_button;border=false;font=bold;font_size=22]", + "style_type[image_button:hovered;fgimg=" .. menutxtrdir .. "mt_menu_button_hov.png]", + "bgcolor[;neither]", + "image[1.25,1;2.5,2.5;" .. menutxtrdir .. "minetesticon.png]", + "container[0.5,7.2]", + "image_button[0,0;4,1;" .. menutxtrdir .. "mt_menu_button.png;play;Gioca!]", + "image_button[0,1.2;4,1;" .. menutxtrdir .. "mt_menu_button.png;settings;Impostazioni]", + "image_button[0,2.4;4,1;" .. menutxtrdir .. "mt_menu_button.png;quit;Esci]", + "container_end[]" +} + +core.update_formspec(table.concat(fs,"")) + +core.button_handler = function(fields) + if fields.play then + gamedata.playername = get_random_name() + + -- TODO: controlla se quel nome già esiste e in caso, generane un altro + + core.start() + + elseif fields.settings then + core.update_formspec(settings_get_formspec({})) + + elseif fields.quit then + core.close() + end end -init_globals() + diff --git a/builtin/mainmenu/async_event.lua b/builtin/mainmenu/old_code/async_event.lua similarity index 100% rename from builtin/mainmenu/async_event.lua rename to builtin/mainmenu/old_code/async_event.lua diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/old_code/common.lua similarity index 89% rename from builtin/mainmenu/common.lua rename to builtin/mainmenu/old_code/common.lua index 16c5c9d957c5..4713605812ea 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/old_code/common.lua @@ -18,26 +18,6 @@ -- Global menu data menudata = {} --- located in user cache path, for remembering this like e.g. last update check -cache_settings = Settings(core.get_cache_path() .. DIR_DELIM .. "common.conf") - ---- Checks if the given key contains a timestamp less than a certain age. ---- Pair this with a call to `cache_settings:set(key, tostring(os.time()))` ---- after successfully refreshing the cache. ---- @param key Name of entry in cache_settings ---- @param max_age Age to check against, in seconds ---- @return true if the max age is not reached -function check_cache_age(key, max_age) - local time_now = os.time() - local time_checked = tonumber(cache_settings:get(key)) or 0 - return time_now - time_checked < 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 - -- Local cached values local min_supp_proto, max_supp_proto @@ -47,16 +27,6 @@ function common_update_cached_supp_proto() end common_update_cached_supp_proto() --- Other global functions - -function core.sound_stop(handle, ...) - return handle:stop(...) -end - -function os.tmpname() - error('do not use') -- instead: core.get_temp_path() -end - -- Menu helper functions local function render_client_count(n) @@ -170,6 +140,11 @@ function render_serverlist_row(spec) return table.concat(details, ",") end +--------------------------------------------------------------------------------- +os.tmpname = function() + error('do not use') -- instead use core.get_temp_path() +end +-------------------------------------------------------------------------------- function menu_render_worldlist() local retval = {} diff --git a/builtin/mainmenu/old_code/content/dlg_contentstore.lua b/builtin/mainmenu/old_code/content/dlg_contentstore.lua new file mode 100644 index 000000000000..e567fcef7a24 --- /dev/null +++ b/builtin/mainmenu/old_code/content/dlg_contentstore.lua @@ -0,0 +1,1223 @@ +--Minetest +--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. + +if not core.get_http_api then + function create_store_dlg() + return messagebox("store", + fgettext("ContentDB is not available when Minetest was compiled without cURL")) + end + return +end + +local store = { + loading = false, + load_ok = false, + load_error = false, + + -- Unordered preserves the original order of the ContentDB API, + -- before the package list is ordered based on installed state. + packages = {}, + packages_full = {}, + packages_full_unordered = {}, + aliases = {}, +} + +local http = core.get_http_api() + +-- Screenshot +local screenshot_dir = core.get_cache_path() .. DIR_DELIM .. "cdb" +assert(core.create_dir(screenshot_dir)) +local screenshot_downloading = {} +local screenshot_downloaded = {} + +-- Filter +local search_string = "" +local cur_page = 1 +local num_per_page = 5 +local filter_type = 1 +local filter_types_titles = { + fgettext("All packages"), + fgettext("Games"), + fgettext("Mods"), + fgettext("Texture packs"), +} + +-- Automatic package installation +local auto_install_spec = nil + +local number_downloading = 0 +local download_queue = {} + +local filter_types_type = { + nil, + "game", + "mod", + "txp", +} + +local REASON_NEW = "new" +local REASON_UPDATE = "update" +local REASON_DEPENDENCY = "dependency" + + +local function get_download_url(package, reason) + local base_url = core.settings:get("contentdb_url") + local ret = base_url .. ("/packages/%s/releases/%d/download/"):format( + package.url_part, package.release) + if reason then + ret = ret .. "?reason=" .. reason + end + return ret +end + + +local function download_and_extract(param) + local package = param.package + + local filename = core.get_temp_path(true) + if filename == "" or not core.download_file(param.url, filename) then + core.log("error", "Downloading " .. dump(param.url) .. " failed") + return { + msg = fgettext_ne("Failed to download \"$1\"", package.title) + } + end + + local tempfolder = core.get_temp_path() + if tempfolder ~= "" then + tempfolder = tempfolder .. DIR_DELIM .. "MT_" .. math.random(1, 1024000) + if not core.extract_zip(filename, tempfolder) then + tempfolder = nil + end + else + tempfolder = nil + end + os.remove(filename) + if not tempfolder then + return { + msg = fgettext_ne("Failed to extract \"$1\" (unsupported file type or broken archive)", package.title), + } + end + + return { + path = tempfolder + } +end + +local function start_install(package, reason) + local params = { + package = package, + url = get_download_url(package, reason), + } + + number_downloading = number_downloading + 1 + + local function callback(result) + if result.msg then + gamedata.errormessage = result.msg + else + local path, msg = pkgmgr.install_dir(package.type, result.path, package.name, package.path) + core.delete_dir(result.path) + if not path then + gamedata.errormessage = fgettext_ne("Error installing \"$1\": $2", package.title, msg) + else + core.log("action", "Installed package to " .. path) + + local conf_path + local name_is_title = false + if package.type == "mod" then + local actual_type = pkgmgr.get_folder_type(path) + if actual_type.type == "modpack" then + conf_path = path .. DIR_DELIM .. "modpack.conf" + else + conf_path = path .. DIR_DELIM .. "mod.conf" + end + elseif package.type == "game" then + conf_path = path .. DIR_DELIM .. "game.conf" + name_is_title = true + elseif package.type == "txp" then + conf_path = path .. DIR_DELIM .. "texture_pack.conf" + end + + if conf_path then + local conf = Settings(conf_path) + conf:set("title", package.title) + if not name_is_title then + conf:set("name", package.name) + end + if not conf:get("description") then + conf:set("description", package.short_description) + end + conf:set("author", package.author) + conf:set("release", package.release) + conf:write() + end + end + end + + package.downloading = false + + number_downloading = number_downloading - 1 + + local next = download_queue[1] + if next then + table.remove(download_queue, 1) + + start_install(next.package, next.reason) + end + + ui.update() + end + + package.queued = false + package.downloading = true + + if not core.handle_async(download_and_extract, params, callback) then + core.log("error", "ERROR: async event failed") + gamedata.errormessage = fgettext_ne("Failed to download $1", package.name) + return + end +end + +local function queue_download(package, reason) + if package.queued or package.downloading then + return + end + + local max_concurrent_downloads = tonumber(core.settings:get("contentdb_max_concurrent_downloads")) + if number_downloading < math.max(max_concurrent_downloads, 1) then + start_install(package, reason) + else + table.insert(download_queue, { package = package, reason = reason }) + package.queued = true + end +end + +local function get_raw_dependencies(package) + if package.type ~= "mod" then + return {} + end + if package.raw_deps then + return package.raw_deps + end + + local url_fmt = "/api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s" + local version = core.get_version() + local base_url = core.settings:get("contentdb_url") + local url = base_url .. url_fmt:format(package.url_part, core.get_max_supp_proto(), core.urlencode(version.string)) + + local response = http.fetch_sync({ url = url }) + if not response.succeeded then + return + end + + local data = core.parse_json(response.data) or {} + + local content_lookup = {} + for _, pkg in pairs(store.packages_full) do + content_lookup[pkg.id] = pkg + end + + for id, raw_deps in pairs(data) do + local package2 = content_lookup[id:lower()] + if package2 and not package2.raw_deps then + package2.raw_deps = raw_deps + + for _, dep in pairs(raw_deps) do + local packages = {} + for i=1, #dep.packages do + packages[#packages + 1] = content_lookup[dep.packages[i]:lower()] + end + dep.packages = packages + end + end + end + + return package.raw_deps +end + +local function has_hard_deps(raw_deps) + for i=1, #raw_deps do + if not raw_deps[i].is_optional then + return true + end + end + + return false +end + +-- Recursively resolve dependencies, given the installed mods +local function resolve_dependencies_2(raw_deps, installed_mods, out) + local function resolve_dep(dep) + -- Check whether it's already installed + if installed_mods[dep.name] then + return { + is_optional = dep.is_optional, + name = dep.name, + installed = true, + } + end + + -- Find exact name matches + local fallback + for _, package in pairs(dep.packages) do + if package.type ~= "game" then + if package.name == dep.name then + return { + is_optional = dep.is_optional, + name = dep.name, + installed = false, + package = package, + } + elseif not fallback then + fallback = package + end + end + end + + -- Otherwise, find the first mod that fulfills it + if fallback then + return { + is_optional = dep.is_optional, + name = dep.name, + installed = false, + package = fallback, + } + end + + return { + is_optional = dep.is_optional, + name = dep.name, + installed = false, + } + end + + for _, dep in pairs(raw_deps) do + if not dep.is_optional and not out[dep.name] then + local result = resolve_dep(dep) + out[dep.name] = result + if result and result.package and not result.installed then + local raw_deps2 = get_raw_dependencies(result.package) + if raw_deps2 then + resolve_dependencies_2(raw_deps2, installed_mods, out) + end + end + end + end + + return true +end + +-- Resolve dependencies for a package, calls the recursive version. +local function resolve_dependencies(raw_deps, game) + assert(game) + + local installed_mods = {} + + local mods = {} + pkgmgr.get_game_mods(game, mods) + for _, mod in pairs(mods) do + installed_mods[mod.name] = true + end + + for _, mod in pairs(pkgmgr.global_mods:get_list()) do + installed_mods[mod.name] = true + end + + local out = {} + if not resolve_dependencies_2(raw_deps, installed_mods, out) then + return nil + end + + local retval = {} + for _, dep in pairs(out) do + retval[#retval + 1] = dep + end + + table.sort(retval, function(a, b) + return a.name < b.name + end) + + return retval +end + +local install_dialog = {} +function install_dialog.get_formspec() + local selected_game, selected_game_idx = pkgmgr.find_by_gameid(core.settings:get("menu_last_game")) + if not selected_game_idx then + selected_game_idx = 1 + selected_game = pkgmgr.games[1] + end + + local game_list = {} + for i, game in ipairs(pkgmgr.games) do + game_list[i] = core.formspec_escape(game.title) + end + + local package = install_dialog.package + local raw_deps = install_dialog.raw_deps + local will_install_deps = install_dialog.will_install_deps + + local deps_to_install = 0 + local deps_not_found = 0 + + install_dialog.dependencies = resolve_dependencies(raw_deps, selected_game) + local formatted_deps = {} + for _, dep in pairs(install_dialog.dependencies) do + formatted_deps[#formatted_deps + 1] = "#fff" + formatted_deps[#formatted_deps + 1] = core.formspec_escape(dep.name) + if dep.installed then + formatted_deps[#formatted_deps + 1] = "#ccf" + formatted_deps[#formatted_deps + 1] = fgettext("Already installed") + elseif dep.package then + formatted_deps[#formatted_deps + 1] = "#cfc" + formatted_deps[#formatted_deps + 1] = fgettext("$1 by $2", dep.package.title, dep.package.author) + deps_to_install = deps_to_install + 1 + else + formatted_deps[#formatted_deps + 1] = "#f00" + formatted_deps[#formatted_deps + 1] = fgettext("Not found") + deps_not_found = deps_not_found + 1 + end + end + + local message_bg = "#3333" + local message + if will_install_deps then + message = fgettext("$1 and $2 dependencies will be installed.", package.title, deps_to_install) + else + message = fgettext("$1 will be installed, and $2 dependencies will be skipped.", package.title, deps_to_install) + end + if deps_not_found > 0 then + message = fgettext("$1 required dependencies could not be found.", deps_not_found) .. + " " .. fgettext("Please check that the base game is correct.", deps_not_found) .. + "\n" .. message + message_bg = mt_color_orange + end + + local formspec = { + "formspec_version[3]", + "size[7,7.85]", + "style[title;border=false]", + "box[0,0;7,0.5;#3333]", + "button[0,0;7,0.5;title;", fgettext("Install $1", package.title) , "]", + + "container[0.375,0.70]", + + "label[0,0.25;", fgettext("Base Game:"), "]", + "dropdown[2,0;4.25,0.5;selected_game;", table.concat(game_list, ","), ";", selected_game_idx, "]", + + "label[0,0.8;", fgettext("Dependencies:"), "]", + + "tablecolumns[color;text;color;text]", + "table[0,1.1;6.25,3;packages;", table.concat(formatted_deps, ","), "]", + + "container_end[]", + + "checkbox[0.375,5.1;will_install_deps;", + fgettext("Install missing dependencies"), ";", + will_install_deps and "true" or "false", "]", + + "box[0,5.4;7,1.2;", message_bg, "]", + "textarea[0.375,5.5;6.25,1;;;", message, "]", + + "container[1.375,6.85]", + "button[0,0;2,0.8;install_all;", fgettext("Install"), "]", + "button[2.25,0;2,0.8;cancel;", fgettext("Cancel"), "]", + "container_end[]", + } + + return table.concat(formspec) +end + +function install_dialog.handle_submit(this, fields) + if fields.cancel then + this:delete() + return true + end + + if fields.will_install_deps ~= nil then + install_dialog.will_install_deps = core.is_yes(fields.will_install_deps) + return true + end + + if fields.install_all then + queue_download(install_dialog.package, REASON_NEW) + + if install_dialog.will_install_deps then + for _, dep in pairs(install_dialog.dependencies) do + if not dep.is_optional and not dep.installed and dep.package then + queue_download(dep.package, REASON_DEPENDENCY) + end + end + end + + this:delete() + return true + end + + if fields.selected_game then + for _, game in pairs(pkgmgr.games) do + if game.title == fields.selected_game then + core.settings:set("menu_last_game", game.id) + break + end + end + return true + end + + return false +end + +function install_dialog.create(package, raw_deps) + install_dialog.dependencies = nil + install_dialog.package = package + install_dialog.raw_deps = raw_deps + install_dialog.will_install_deps = true + return dialog_create("install_dialog", + install_dialog.get_formspec, + install_dialog.handle_submit, + nil) +end + + +local confirm_overwrite = {} +function confirm_overwrite.get_formspec() + local package = confirm_overwrite.package + + return confirmation_formspec( + fgettext("\"$1\" already exists. Would you like to overwrite it?", package.name), + 'install', fgettext("Overwrite"), + 'cancel', fgettext("Cancel")) +end + +function confirm_overwrite.handle_submit(this, fields) + if fields.cancel then + this:delete() + return true + end + + if fields.install then + this:delete() + confirm_overwrite.callback() + return true + end + + return false +end + +function confirm_overwrite.create(package, callback) + assert(type(package) == "table") + assert(type(callback) == "function") + + confirm_overwrite.package = package + confirm_overwrite.callback = callback + return dialog_create("confirm_overwrite", + confirm_overwrite.get_formspec, + confirm_overwrite.handle_submit, + nil) +end + +local function install_or_update_package(this, package) + local install_parent + if package.type == "mod" then + install_parent = core.get_modpath() + elseif package.type == "game" then + install_parent = core.get_gamepath() + elseif package.type == "txp" then + install_parent = core.get_texturepath() + else + error("Unknown package type: " .. package.type) + end + + if package.queued or package.downloading then + return + end + + local function on_confirm() + local deps = get_raw_dependencies(package) + if deps and has_hard_deps(deps) then + local dlg = install_dialog.create(package, deps) + dlg:set_parent(this) + this:hide() + dlg:show() + else + queue_download(package, package.path and REASON_UPDATE or REASON_NEW) + end + end + + if package.type == "mod" and #pkgmgr.games == 0 then + local dlg = messagebox("install_game", + fgettext("You need to install a game before you can install a mod")) + dlg:set_parent(this) + this:hide() + dlg:show() + elseif not package.path and core.is_dir(install_parent .. DIR_DELIM .. package.name) then + local dlg = confirm_overwrite.create(package, on_confirm) + dlg:set_parent(this) + this:hide() + dlg:show() + else + on_confirm() + end +end + + +local function get_file_extension(path) + local parts = path:split(".") + return parts[#parts] +end + +local function get_screenshot(package) + if not package.thumbnail then + return defaulttexturedir .. "no_screenshot.png" + elseif screenshot_downloading[package.thumbnail] then + return defaulttexturedir .. "loading_screenshot.png" + end + + -- Get tmp screenshot path + local ext = get_file_extension(package.thumbnail) + local filepath = screenshot_dir .. DIR_DELIM .. + ("%s-%s-%s.%s"):format(package.type, package.author, package.name, ext) + + -- Return if already downloaded + local file = io.open(filepath, "r") + if file then + file:close() + return filepath + end + + -- Show error if we've failed to download before + if screenshot_downloaded[package.thumbnail] then + return defaulttexturedir .. "error_screenshot.png" + end + + -- Download + + local function download_screenshot(params) + return core.download_file(params.url, params.dest) + end + local function callback(success) + screenshot_downloading[package.thumbnail] = nil + screenshot_downloaded[package.thumbnail] = true + if not success then + core.log("warning", "Screenshot download failed for some reason") + end + ui.update() + end + if core.handle_async(download_screenshot, + { dest = filepath, url = package.thumbnail }, callback) then + screenshot_downloading[package.thumbnail] = true + else + core.log("error", "ERROR: async event failed") + return defaulttexturedir .. "error_screenshot.png" + end + + return defaulttexturedir .. "loading_screenshot.png" +end + +local function fetch_pkgs() + local version = core.get_version() + local base_url = core.settings:get("contentdb_url") + 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 + url = url .. "&hide=" .. core.urlencode(item) + end + end + + local http = core.get_http_api() + local response = http.fetch_sync({ url = url }) + if not response.succeeded then + return + end + + local packages = core.parse_json(response.data) + if not packages or #packages == 0 then + return + end + local aliases = {} + + for _, package in pairs(packages) do + local name_len = #package.name + -- This must match what store.update_paths() does! + package.id = package.author:lower() .. "/" + if package.type == "game" and name_len > 5 and package.name:sub(name_len - 4) == "_game" then + package.id = package.id .. package.name:sub(1, name_len - 5) + else + package.id = package.id .. package.name + end + + package.url_part = core.urlencode(package.author) .. "/" .. core.urlencode(package.name) + + if package.aliases then + for _, alias in ipairs(package.aliases) do + -- We currently don't support name changing + local suffix = "/" .. package.name + if alias:sub(-#suffix) == suffix then + aliases[alias:lower()] = package.id + end + end + end + end + + return { packages = packages, aliases = aliases } +end + +local function sort_and_filter_pkgs() + store.update_paths() + store.sort_packages() + store.filter_packages(search_string) +end + +-- Resolves the package specification stored in auto_install_spec into an actual package. +-- May only be called after the package list has been loaded successfully. +local function resolve_auto_install_spec() + assert(store.load_ok) + + if not auto_install_spec then + return nil + end + + local spec = store.aliases[auto_install_spec] or auto_install_spec + local resolved = nil + + for _, pkg in ipairs(store.packages_full_unordered) do + if pkg.id == spec then + resolved = pkg + break + end + end + + if not resolved then + gamedata.errormessage = fgettext("The package $1 was not found.", auto_install_spec) + ui.update() + + auto_install_spec = nil + end + + return resolved +end + +-- Installs the package specified by auto_install_spec. +-- Only does something if: +-- a. The package list has been loaded successfully. +-- b. The store dialog is currently visible. +local function do_auto_install() + if not store.load_ok then + return + end + + local pkg = resolve_auto_install_spec() + if not pkg then + return + end + + local store_dlg = ui.find_by_name("store") + if not store_dlg or store_dlg.hidden then + return + end + + install_or_update_package(store_dlg, pkg) + auto_install_spec = nil +end + +function store.load() + if store.load_ok then + sort_and_filter_pkgs() + return + end + if store.loading then + return + end + store.loading = true + core.handle_async( + fetch_pkgs, + nil, + function(result) + if result then + store.load_ok = true + store.load_error = false + store.packages = result.packages + store.packages_full = result.packages + store.packages_full_unordered = result.packages + store.aliases = result.aliases + + sort_and_filter_pkgs() + do_auto_install() + else + store.load_error = true + end + + store.loading = false + ui.update() + end + ) +end + +function store.update_paths() + local mod_hash = {} + pkgmgr.refresh_globals() + for _, mod in pairs(pkgmgr.global_mods:get_list()) do + local cdb_id = pkgmgr.get_contentdb_id(mod) + if cdb_id then + mod_hash[store.aliases[cdb_id] or cdb_id] = mod + end + end + + local game_hash = {} + pkgmgr.update_gamelist() + for _, game in pairs(pkgmgr.games) do + local cdb_id = pkgmgr.get_contentdb_id(game) + if cdb_id then + game_hash[store.aliases[cdb_id] or cdb_id] = game + end + end + + local txp_hash = {} + for _, txp in pairs(pkgmgr.get_texture_packs()) do + local cdb_id = pkgmgr.get_contentdb_id(txp) + if cdb_id then + txp_hash[store.aliases[cdb_id] or cdb_id] = txp + end + end + + for _, package in pairs(store.packages_full) do + local content + if package.type == "mod" then + content = mod_hash[package.id] + elseif package.type == "game" then + content = game_hash[package.id] + elseif package.type == "txp" then + content = txp_hash[package.id] + end + + if content then + package.path = content.path + package.installed_release = content.release or 0 + else + package.path = nil + package.installed_release = nil + end + end +end + +function store.sort_packages() + local ret = {} + + local auto_install_pkg = resolve_auto_install_spec() -- can be nil + + -- Add installed content + for _, pkg in ipairs(store.packages_full_unordered) do + if pkg.path and pkg ~= auto_install_pkg then + ret[#ret + 1] = pkg + end + end + + -- Sort installed content first by "is there an update available?", then by title + table.sort(ret, function(a, b) + local a_updatable = a.installed_release < a.release + local b_updatable = b.installed_release < b.release + if a_updatable and not b_updatable then + return true + elseif b_updatable and not a_updatable then + return false + end + + return a.title < b.title + end) + + -- Add uninstalled content + for _, pkg in ipairs(store.packages_full_unordered) do + if not pkg.path and pkg ~= auto_install_pkg then + ret[#ret + 1] = pkg + end + end + + -- Put the package that will be auto-installed at the very top + if auto_install_pkg then + table.insert(ret, 1, auto_install_pkg) + end + + store.packages_full = ret +end + +function store.filter_packages(query) + if query == "" and filter_type == 1 then + store.packages = store.packages_full + return + end + + local keywords = {} + for word in query:lower():gmatch("%S+") do + table.insert(keywords, word) + end + + local function matches_keywords(package) + for k = 1, #keywords do + local keyword = keywords[k] + + if string.find(package.name:lower(), keyword, 1, true) or + string.find(package.title:lower(), keyword, 1, true) or + string.find(package.author:lower(), keyword, 1, true) or + string.find(package.short_description:lower(), keyword, 1, true) then + return true + end + end + + return false + end + + store.packages = {} + for _, package in pairs(store.packages_full) do + if (query == "" or matches_keywords(package)) and + (filter_type == 1 or package.type == filter_types_type[filter_type]) then + store.packages[#store.packages + 1] = package + end + end +end + +local function get_info_formspec(text) + local H = 9.5 + return table.concat({ + "formspec_version[6]", + "size[15.75,9.5]", + TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "position[0.5,0.55]", + + "label[4,4.35;", text, "]", + "container[0,", H - 0.8 - 0.375, "]", + "button[0.375,0;5,0.8;back;", fgettext("Back to Main Menu"), "]", + "container_end[]", + }) +end + +function store.get_formspec(dlgdata) + if store.loading then + return get_info_formspec(fgettext("Loading...")) + end + if store.load_error then + return get_info_formspec(fgettext("No packages could be retrieved")) + end + assert(store.load_ok) + + store.update_paths() + + dlgdata.pagemax = math.max(math.ceil(#store.packages / num_per_page), 1) + if cur_page > dlgdata.pagemax then + cur_page = 1 + end + + local W = 15.75 + local H = 9.5 + local formspec = { + "formspec_version[6]", + "size[15.75,9.5]", + TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "position[0.5,0.55]", + + "style[status,downloading,queued;border=false]", + + "container[0.375,0.375]", + "field[0,0;7.225,0.8;search_string;;", core.formspec_escape(search_string), "]", + "field_enter_after_edit[search_string;true]", + "image_button[7.3,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]", + "image_button[8.125,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";clear;]", + "dropdown[9.175,0;2.7875,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]", + "container_end[]", + + -- Page nav buttons + "container[0,", H - 0.8 - 0.375, "]", + "button[0.375,0;5,0.8;back;", fgettext("Back to Main Menu"), "]", + + "container[", W - 0.375 - 0.8*4 - 2, ",0]", + "image_button[0,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "start_icon.png;pstart;]", + "image_button[0.8,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "prev_icon.png;pback;]", + "style[pagenum;border=false]", + "button[1.6,0;2,0.8;pagenum;", tonumber(cur_page), " / ", tonumber(dlgdata.pagemax), "]", + "image_button[3.6,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "next_icon.png;pnext;]", + "image_button[4.4,0;0.8,0.8;", core.formspec_escape(defaulttexturedir), "end_icon.png;pend;]", + "container_end[]", + + "container_end[]", + } + + if number_downloading > 0 then + formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;downloading;" + if #download_queue > 0 then + formspec[#formspec + 1] = fgettext("$1 downloading,\n$2 queued", number_downloading, #download_queue) + else + formspec[#formspec + 1] = fgettext("$1 downloading...", number_downloading) + end + formspec[#formspec + 1] = "]" + else + local num_avail_updates = 0 + for i=1, #store.packages_full do + local package = store.packages_full[i] + if package.path and package.installed_release < package.release and + not (package.downloading or package.queued) then + num_avail_updates = num_avail_updates + 1 + end + end + + if num_avail_updates == 0 then + formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;status;" + formspec[#formspec + 1] = fgettext("No updates") + formspec[#formspec + 1] = "]" + else + formspec[#formspec + 1] = "button[12.5875,0.375;2.7875,0.8;update_all;" + formspec[#formspec + 1] = fgettext("Update All [$1]", num_avail_updates) + formspec[#formspec + 1] = "]" + end + end + + if #store.packages == 0 then + formspec[#formspec + 1] = "label[4,4.75;" + formspec[#formspec + 1] = fgettext("No results") + formspec[#formspec + 1] = "]" + end + + -- download/queued tooltips always have the same message + local tooltip_colors = ";#dff6f5;#302c2e]" + formspec[#formspec + 1] = "tooltip[downloading;" .. fgettext("Downloading...") .. tooltip_colors + formspec[#formspec + 1] = "tooltip[queued;" .. fgettext("Queued") .. tooltip_colors + + local start_idx = (cur_page - 1) * num_per_page + 1 + for i=start_idx, math.min(#store.packages, start_idx+num_per_page-1) do + local package = store.packages[i] + local container_y = (i - start_idx) * 1.375 + (2*0.375 + 0.8) + formspec[#formspec + 1] = "container[0.375," + formspec[#formspec + 1] = container_y + formspec[#formspec + 1] = "]" + + -- image + formspec[#formspec + 1] = "image[0,0;1.5,1;" + formspec[#formspec + 1] = core.formspec_escape(get_screenshot(package)) + formspec[#formspec + 1] = "]" + + -- title + formspec[#formspec + 1] = "label[1.875,0.1;" + formspec[#formspec + 1] = core.formspec_escape( + core.colorize(mt_color_green, package.title) .. + core.colorize("#BFBFBF", " by " .. package.author)) + formspec[#formspec + 1] = "]" + + -- buttons + local description_width = W - 2.625 - 2 * 0.7 - 2 * 0.15 + + local second_base = "image_button[-1.55,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir) + local third_base = "image_button[-2.4,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir) + formspec[#formspec + 1] = "container[" + formspec[#formspec + 1] = W - 0.375*2 + formspec[#formspec + 1] = ",0.1]" + + if package.downloading then + formspec[#formspec + 1] = "animated_image[-1.7,-0.15;1,1;downloading;" + formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir) + formspec[#formspec + 1] = "cdb_downloading.png;3;400;]" + elseif package.queued then + formspec[#formspec + 1] = second_base + formspec[#formspec + 1] = "cdb_queued.png;queued;]" + elseif not package.path then + local elem_name = "install_" .. i .. ";" + formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#71aa34]" + formspec[#formspec + 1] = second_base .. "cdb_add.png;" .. elem_name .. "]" + formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Install") .. tooltip_colors + else + if package.installed_release < package.release then + -- The install_ action also handles updating + local elem_name = "install_" .. i .. ";" + formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#28ccdf]" + formspec[#formspec + 1] = third_base .. "cdb_update.png;" .. elem_name .. "]" + formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Update") .. tooltip_colors + + description_width = description_width - 0.7 - 0.15 + end + + local elem_name = "uninstall_" .. i .. ";" + formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#a93b3b]" + formspec[#formspec + 1] = second_base .. "cdb_clear.png;" .. elem_name .. "]" + formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Uninstall") .. tooltip_colors + end + + local web_elem_name = "view_" .. i .. ";" + formspec[#formspec + 1] = "image_button[-0.7,0;0.7,0.7;" .. + core.formspec_escape(defaulttexturedir) .. "cdb_viewonline.png;" .. web_elem_name .. "]" + formspec[#formspec + 1] = "tooltip[" .. web_elem_name .. + fgettext("View more information in a web browser") .. tooltip_colors + formspec[#formspec + 1] = "container_end[]" + + -- description + formspec[#formspec + 1] = "textarea[1.855,0.3;" + formspec[#formspec + 1] = tostring(description_width) + formspec[#formspec + 1] = ",0.8;;;" + formspec[#formspec + 1] = core.formspec_escape(package.short_description) + formspec[#formspec + 1] = "]" + + formspec[#formspec + 1] = "container_end[]" + end + + return table.concat(formspec) +end + +function store.handle_submit(this, fields) + if fields.search or fields.key_enter_field == "search_string" then + search_string = fields.search_string:trim() + cur_page = 1 + store.filter_packages(search_string) + return true + end + + if fields.clear then + search_string = "" + cur_page = 1 + store.filter_packages("") + return true + end + + if fields.back then + this:delete() + return true + end + + if fields.pstart then + cur_page = 1 + return true + end + + if fields.pend then + cur_page = this.data.pagemax + return true + end + + if fields.pnext then + cur_page = cur_page + 1 + if cur_page > this.data.pagemax then + cur_page = 1 + end + return true + end + + if fields.pback then + if cur_page == 1 then + cur_page = this.data.pagemax + else + cur_page = cur_page - 1 + end + return true + end + + if fields.type then + local new_type = table.indexof(filter_types_titles, fields.type) + if new_type ~= filter_type then + filter_type = new_type + cur_page = 1 + store.filter_packages(search_string) + return true + end + end + + if fields.update_all then + for i=1, #store.packages_full do + local package = store.packages_full[i] + if package.path and package.installed_release < package.release and + not (package.downloading or package.queued) then + queue_download(package, REASON_UPDATE) + end + end + return true + end + + local start_idx = (cur_page - 1) * num_per_page + 1 + assert(start_idx ~= nil) + for i=start_idx, math.min(#store.packages, start_idx+num_per_page-1) do + local package = store.packages[i] + assert(package) + + if fields["install_" .. i] then + install_or_update_package(this, package) + return true + end + + if fields["uninstall_" .. i] then + local dlg = create_delete_content_dlg(package) + dlg:set_parent(this) + this:hide() + dlg:show() + return true + end + + if fields["view_" .. i] then + local url = ("%s/packages/%s?protocol_version=%d"):format( + core.settings:get("contentdb_url"), package.url_part, + core.get_max_supp_proto()) + core.open_url(url) + return true + end + end + + return false +end + +function store.handle_events(event) + if event == "DialogShow" then + -- On mobile, don't show the "MINETEST" header behind the dialog. + mm_game_theme.set_engine(TOUCHSCREEN_GUI) + + -- If the store is already loaded, auto-install packages here. + do_auto_install() + + return true + end + + return false +end + +--- Creates a ContentDB dialog. +--- +--- @param type string | nil +--- Sets initial package filter. "game", "mod", "txp" or nil (no filter). +--- @param install_spec table | nil +--- ContentDB ID of package as returned by pkgmgr.get_contentdb_id(). +--- Sets package to install or update automatically. +function create_store_dlg(type, install_spec) + search_string = "" + cur_page = 1 + if type then + -- table.indexof does not work on tables that contain `nil` + for i, v in pairs(filter_types_type) do + if v == type then + filter_type = i + break + end + end + else + filter_type = 1 + end + + -- Keep the old auto_install_spec if the caller doesn't specify one. + if install_spec then + auto_install_spec = install_spec + end + + store.load() + + return dialog_create("store", + store.get_formspec, + store.handle_submit, + store.handle_events) +end diff --git a/builtin/mainmenu/content/init.lua b/builtin/mainmenu/old_code/content/init.lua similarity index 79% rename from builtin/mainmenu/content/init.lua rename to builtin/mainmenu/old_code/content/init.lua index e66828089af0..9b562fedc428 100644 --- a/builtin/mainmenu/content/init.lua +++ b/builtin/mainmenu/old_code/content/init.lua @@ -18,9 +18,5 @@ local path = core.get_mainmenu_path() .. DIR_DELIM .. "content" dofile(path .. DIR_DELIM .. "pkgmgr.lua") -dofile(path .. DIR_DELIM .. "contentdb.lua") dofile(path .. DIR_DELIM .. "update_detector.lua") -dofile(path .. DIR_DELIM .. "screenshots.lua") -dofile(path .. DIR_DELIM .. "dlg_install.lua") -dofile(path .. DIR_DELIM .. "dlg_overwrite.lua") -dofile(path .. DIR_DELIM .. "dlg_contentdb.lua") +dofile(path .. DIR_DELIM .. "dlg_contentstore.lua") diff --git a/builtin/mainmenu/content/pkgmgr.lua b/builtin/mainmenu/old_code/content/pkgmgr.lua similarity index 92% rename from builtin/mainmenu/content/pkgmgr.lua rename to builtin/mainmenu/old_code/content/pkgmgr.lua index 5a4aa6c17451..9408eb994ef2 100644 --- a/builtin/mainmenu/content/pkgmgr.lua +++ b/builtin/mainmenu/old_code/content/pkgmgr.lua @@ -110,7 +110,7 @@ pkgmgr = {} -- @param modpack Currently processing modpack or nil/"" if none (recursion) function pkgmgr.get_mods(path, virtual_path, listing, modpack) local mods = core.get_dir_list(path, true) - local added = {} + for _, name in ipairs(mods) do if name:sub(1, 1) ~= "." then local mod_path = path .. DIR_DELIM .. name @@ -120,7 +120,6 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack) parent_dir = path, } listing[#listing + 1] = toadd - added[#added + 1] = toadd -- Get config file local mod_conf @@ -170,8 +169,6 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack) end end - pkgmgr.update_translations(added) - if not modpack then -- Sort all when the recursion is done table.sort(listing, function(a, b) @@ -181,31 +178,26 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack) end -------------------------------------------------------------------------------- -function pkgmgr.reload_texture_packs() +function pkgmgr.get_texture_packs() local txtpath = core.get_texturepath() local txtpath_system = core.get_texturepath_share() local retval = {} load_texture_packs(txtpath, retval) - -- on portable versions these two paths coincide. It avoids loading the path twice if txtpath ~= txtpath_system then load_texture_packs(txtpath_system, retval) end - pkgmgr.update_translations(retval) - table.sort(retval, function(a, b) return a.title:lower() < b.title:lower() end) - pkgmgr.texture_packs = retval + return retval end -------------------------------------------------------------------------------- function pkgmgr.get_all() - pkgmgr.load_all() - local result = {} for _, mod in pairs(pkgmgr.global_mods:get_list()) do @@ -214,7 +206,7 @@ function pkgmgr.get_all() for _, game in pairs(pkgmgr.games) do result[#result + 1] = game end - for _, txp in pairs(pkgmgr.texture_packs) do + for _, txp in pairs(pkgmgr.get_texture_packs()) do result[#result + 1] = txp end @@ -292,7 +284,7 @@ end function pkgmgr.render_packagelist(render_list, use_technical_names, with_icon) if not render_list then if not pkgmgr.global_mods then - pkgmgr.reload_global_mods() + pkgmgr.refresh_globals() end render_list = pkgmgr.global_mods end @@ -553,7 +545,6 @@ function pkgmgr.get_worldconfig(worldpath) end -------------------------------------------------------------------------------- --- Caller is responsible for reloading content types (see reload_by_type) function pkgmgr.install_dir(expected_type, path, basename, targetpath) assert(type(expected_type) == "string") assert(type(path) == "string") @@ -620,6 +611,12 @@ function pkgmgr.install_dir(expected_type, path, basename, targetpath) fgettext_ne("Failed to install $1 to $2", basename, targetpath) end + if basefolder.type == "game" then + pkgmgr.update_gamelist() + else + pkgmgr.refresh_globals() + end + return targetpath, nil end @@ -741,7 +738,7 @@ function pkgmgr.comparemod(elem1,elem2) end -------------------------------------------------------------------------------- -function pkgmgr.reload_global_mods() +function pkgmgr.refresh_globals() local function is_equal(element,uid) --uid match if element.name == uid then return true @@ -773,60 +770,11 @@ function pkgmgr.get_game_mods(gamespec, retval) end -------------------------------------------------------------------------------- -function pkgmgr.reload_games() +function pkgmgr.update_gamelist() pkgmgr.games = core.get_games() table.sort(pkgmgr.games, function(a, b) return a.title:lower() < b.title:lower() end) - pkgmgr.update_translations(pkgmgr.games) -end - --------------------------------------------------------------------------------- -function pkgmgr.reload_by_type(type) - if type == "game" then - pkgmgr.reload_games() - elseif type == "txp" then - pkgmgr.reload_texture_packs() - elseif type == "mod" or type == "modpack" then - pkgmgr.reload_global_mods() - else - error("Unknown package type: " .. type) - end -end - --------------------------------------------------------------------------------- -function pkgmgr.load_all() - if not pkgmgr.global_mods then - pkgmgr.reload_global_mods() - end - if not pkgmgr.games then - pkgmgr.reload_games() - end - if not pkgmgr.texture_packs then - pkgmgr.reload_texture_packs() - end -end - --------------------------------------------------------------------------------- -function pkgmgr.update_translations(list) - for _, item in ipairs(list) do - local info = core.get_content_info(item.path) - assert(info.path) - assert(info.textdomain) - - assert(not item.is_translated) - item.is_translated = true - - if info.title and info.title ~= "" then - item.title = core.get_content_translation(info.path, info.textdomain, - core.translate(info.textdomain, info.title)) - end - - if info.description and info.description ~= "" then - item.description = core.get_content_translation(info.path, info.textdomain, - core.translate(info.textdomain, info.description)) - end - end end -------------------------------------------------------------------------------- @@ -856,4 +804,4 @@ end -------------------------------------------------------------------------------- -- read initial data -------------------------------------------------------------------------------- -pkgmgr.reload_games() +pkgmgr.update_gamelist() diff --git a/builtin/mainmenu/content/tests/pkgmgr_spec.lua b/builtin/mainmenu/old_code/content/tests/pkgmgr_spec.lua similarity index 95% rename from builtin/mainmenu/content/tests/pkgmgr_spec.lua rename to builtin/mainmenu/old_code/content/tests/pkgmgr_spec.lua index ccadde38c1ef..37c1bb784495 100644 --- a/builtin/mainmenu/content/tests/pkgmgr_spec.lua +++ b/builtin/mainmenu/old_code/content/tests/pkgmgr_spec.lua @@ -46,9 +46,6 @@ local function reset() function core.get_texturepath() return txp_dir end - function core.get_texturepath_share() - return txp_dir - end function core.get_modpath() return mods_dir end @@ -62,6 +59,13 @@ local function reset() setfenv(loadfile("builtin/common/misc_helpers.lua"), env)() setfenv(loadfile("builtin/mainmenu/content/pkgmgr.lua"), env)() + function env.pkgmgr.update_gamelist() + table.insert(calls, { "update_gamelist" }) + end + function env.pkgmgr.refresh_globals() + table.insert(calls, { "refresh_globals" }) + end + function env.assert_calls(list) assert.are.same(list, calls) end @@ -109,6 +113,7 @@ describe("install_dir", function() env.assert_calls({ { "delete_dir", mods_dir .. "/mymod" }, { "copy_dir", "/tmp/123", mods_dir .. "/mymod", false }, + { "refresh_globals" }, }) end) @@ -124,6 +129,7 @@ describe("install_dir", function() env.assert_calls({ { "delete_dir", mods_dir .. "/mymod" }, { "copy_dir", "/tmp/123", mods_dir .. "/mymod", false }, + { "refresh_globals" }, }) end) @@ -139,6 +145,7 @@ describe("install_dir", function() env.assert_calls({ { "delete_dir", games_dir .. "/mygame" }, { "copy_dir", "/tmp/123", games_dir .. "/mygame", false }, + { "update_gamelist" }, }) end) @@ -154,6 +161,7 @@ describe("install_dir", function() env.assert_calls({ { "delete_dir", mods_dir .. "/123" }, { "copy_dir", "/tmp/123", mods_dir .. "/123", false }, + { "refresh_globals" }, }) end) @@ -180,6 +188,7 @@ describe("install_dir", function() env.assert_calls({ { "delete_dir", "/tmp/alt-target" }, { "copy_dir", "/tmp/123", "/tmp/alt-target", false }, + { "refresh_globals" }, }) end) @@ -229,5 +238,6 @@ describe("install_dir", function() path, message = env.pkgmgr.install_dir("txp", "/tmp/123", "name", nil) assert.is._not._nil(path) assert.is._nil(message) + end) end) diff --git a/builtin/mainmenu/content/update_detector.lua b/builtin/mainmenu/old_code/content/update_detector.lua similarity index 83% rename from builtin/mainmenu/content/update_detector.lua rename to builtin/mainmenu/old_code/content/update_detector.lua index 1f8672bbac32..d184272e418d 100644 --- a/builtin/mainmenu/content/update_detector.lua +++ b/builtin/mainmenu/old_code/content/update_detector.lua @@ -26,25 +26,8 @@ if not core.get_http_api then end -assert(core.create_dir(core.get_cache_path() .. DIR_DELIM .. "cdb")) -local cache_file_path = core.get_cache_path() .. DIR_DELIM .. "cdb" .. DIR_DELIM .. "updates.json" local has_fetched = false local latest_releases -do - if check_cache_age("cdb_updates_last_checked", 24 * 3600) then - local f = io.open(cache_file_path, "r") - local data = "" - if f then - data = f:read("*a") - f:close() - end - data = data ~= "" and core.parse_json(data) or nil - if type(data) == "table" then - latest_releases = data - has_fetched = true - end - end -end local function fetch_latest_releases() @@ -72,6 +55,9 @@ end local function has_packages_from_cdb() + pkgmgr.refresh_globals() + pkgmgr.update_gamelist() + for _, content in pairs(pkgmgr.get_all()) do if pkgmgr.get_contentdb_id(content) then return true @@ -103,10 +89,8 @@ local function fetch() has_fetched = false return end - latest_releases = lowercase_keys(releases) - core.safe_file_write(cache_file_path, core.write_json(latest_releases)) - cache_settings:set("cdb_updates_last_checked", tostring(os.time())) + latest_releases = lowercase_keys(releases) if update_detector.get_count() > 0 then local maintab = ui.find_by_name("maintab") if not maintab.hidden then @@ -124,6 +108,9 @@ function update_detector.get_all() return {} end + pkgmgr.refresh_globals() + pkgmgr.update_gamelist() + local ret = {} local all_content = pkgmgr.get_all() for _, content in ipairs(all_content) do diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/old_code/dlg_config_world.lua similarity index 99% rename from builtin/mainmenu/dlg_config_world.lua rename to builtin/mainmenu/old_code/dlg_config_world.lua index e8f49b230aa2..1cc107017e08 100644 --- a/builtin/mainmenu/dlg_config_world.lua +++ b/builtin/mainmenu/old_code/dlg_config_world.lua @@ -334,7 +334,7 @@ local function handle_buttons(this, fields) if fields.btn_config_world_cdb then this.data.list = nil - local dlg = create_contentdb_dlg("mod") + local dlg = create_store_dlg("mod") dlg:set_parent(this) this:hide() dlg:show() diff --git a/builtin/mainmenu/dlg_create_world.lua b/builtin/mainmenu/old_code/dlg_create_world.lua similarity index 97% rename from builtin/mainmenu/dlg_create_world.lua rename to builtin/mainmenu/old_code/dlg_create_world.lua index 772025a254f3..b844923ed569 100644 --- a/builtin/mainmenu/dlg_create_world.lua +++ b/builtin/mainmenu/old_code/dlg_create_world.lua @@ -70,8 +70,6 @@ local flag_checkboxes = { { "trees", fgettext("Trees and jungle grass") }, { "flat", fgettext("Flat terrain") }, { "mudflow", fgettext("Mud flow"), fgettext("Terrain surface erosion") }, - { "temples", fgettext("Desert temples"), - fgettext("Different dungeon variant generated in desert biomes (only if dungeons enabled)") }, -- Biome settings are in mgv6_biomes below }, } @@ -281,7 +279,7 @@ local function create_world_formspec(dialogdata) end local retval = - "size[12.25,7.4,true]" .. + "size[12.25,7,true]" .. -- Left side "container[0,0]".. @@ -323,10 +321,8 @@ local function create_world_formspec(dialogdata) "container_end[]".. -- Menu buttons - "container[0,6.9]".. - "button[3.25,0;3,0.5;world_create_confirm;" .. fgettext("Create") .. "]" .. - "button[6.25,0;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" .. - "container_end[]" + "button[3.25,6.5;3,0.5;world_create_confirm;" .. fgettext("Create") .. "]" .. + "button[6.25,6.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" return retval @@ -335,7 +331,7 @@ end local function create_world_buttonhandler(this, fields) if fields["world_create_open_cdb"] then - local dlg = create_contentdb_dlg("game") + local dlg = create_store_dlg("game") dlg:set_parent(this.parent) this:delete() this.parent:hide() diff --git a/builtin/mainmenu/dlg_delete_content.lua b/builtin/mainmenu/old_code/dlg_delete_content.lua similarity index 95% rename from builtin/mainmenu/dlg_delete_content.lua rename to builtin/mainmenu/old_code/dlg_delete_content.lua index 98e15bf01e1c..1a6178acc741 100644 --- a/builtin/mainmenu/dlg_delete_content.lua +++ b/builtin/mainmenu/old_code/dlg_delete_content.lua @@ -37,7 +37,11 @@ local function delete_content_buttonhandler(this, fields) gamedata.errormessage = fgettext_ne("pkgmgr: failed to delete \"$1\"", this.data.content.path) end - pkgmgr.reload_by_type(this.data.content.type) + if this.data.content.type == "game" then + pkgmgr.update_gamelist() + else + pkgmgr.refresh_globals() + end else gamedata.errormessage = fgettext_ne("pkgmgr: invalid path \"$1\"", this.data.content.path) end diff --git a/builtin/mainmenu/dlg_delete_world.lua b/builtin/mainmenu/old_code/dlg_delete_world.lua similarity index 100% rename from builtin/mainmenu/dlg_delete_world.lua rename to builtin/mainmenu/old_code/dlg_delete_world.lua diff --git a/builtin/mainmenu/dlg_register.lua b/builtin/mainmenu/old_code/dlg_register.lua similarity index 100% rename from builtin/mainmenu/dlg_register.lua rename to builtin/mainmenu/old_code/dlg_register.lua diff --git a/builtin/mainmenu/dlg_reinstall_mtg.lua b/builtin/mainmenu/old_code/dlg_reinstall_mtg.lua similarity index 79% rename from builtin/mainmenu/dlg_reinstall_mtg.lua rename to builtin/mainmenu/old_code/dlg_reinstall_mtg.lua index 685847024024..77652e968eaa 100644 --- a/builtin/mainmenu/dlg_reinstall_mtg.lua +++ b/builtin/mainmenu/old_code/dlg_reinstall_mtg.lua @@ -15,30 +15,15 @@ --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ----- 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 in case it's not obvious ----- ---- - -local SETTING_NAME = "no_mtg_notification" - function check_reinstall_mtg() - -- used to be in minetest.conf - if core.settings:get_bool(SETTING_NAME) then - cache_settings:set_bool(SETTING_NAME, true) - core.settings:remove(SETTING_NAME) - end - - if cache_settings:get_bool(SETTING_NAME) then + if core.settings:get_bool("no_mtg_notification") then return end local games = core.get_games() for _, game in ipairs(games) do if game.id == "minetest" then - cache_settings:set_bool(SETTING_NAME, true) + core.settings:set_bool("no_mtg_notification", true) return end end @@ -52,7 +37,7 @@ function check_reinstall_mtg() end end if not mtg_world_found then - cache_settings:set_bool(SETTING_NAME, true) + core.settings:set_bool("no_mtg_notification", true) return end @@ -93,7 +78,7 @@ local function buttonhandler(this, fields) local maintab = ui.find_by_name("maintab") - local dlg = create_contentdb_dlg(nil, "minetest/minetest") + local dlg = create_store_dlg(nil, "minetest/minetest") dlg:set_parent(maintab) maintab:hide() dlg:show() @@ -102,7 +87,7 @@ local function buttonhandler(this, fields) end if fields.dismiss then - cache_settings:set_bool("no_mtg_notification", true) + core.settings:set_bool("no_mtg_notification", true) this:delete() return true end @@ -126,3 +111,5 @@ function create_reinstall_mtg_dlg() buttonhandler, eventhandler) return dlg end + + diff --git a/builtin/mainmenu/dlg_rename_modpack.lua b/builtin/mainmenu/old_code/dlg_rename_modpack.lua similarity index 98% rename from builtin/mainmenu/dlg_rename_modpack.lua rename to builtin/mainmenu/old_code/dlg_rename_modpack.lua index 884140eb3da2..ca76a8cbecc1 100644 --- a/builtin/mainmenu/dlg_rename_modpack.lua +++ b/builtin/mainmenu/old_code/dlg_rename_modpack.lua @@ -45,7 +45,7 @@ local function rename_modpack_buttonhandler(this, fields) local oldpath = this.data.mod.path local targetpath = this.data.mod.parent_dir .. DIR_DELIM .. fields["te_modpack_name"] os.rename(oldpath, targetpath) - pkgmgr.reload_global_mods() + pkgmgr.refresh_globals() pkgmgr.selected_mod = pkgmgr.global_mods:get_current_index( pkgmgr.global_mods:raw_index_by_uid(fields["te_modpack_name"])) diff --git a/builtin/mainmenu/dlg_version_info.lua b/builtin/mainmenu/old_code/dlg_version_info.lua similarity index 88% rename from builtin/mainmenu/dlg_version_info.lua rename to builtin/mainmenu/old_code/dlg_version_info.lua index 483cd30bd100..98085ee4aac9 100644 --- a/builtin/mainmenu/dlg_version_info.lua +++ b/builtin/mainmenu/old_code/dlg_version_info.lua @@ -51,13 +51,12 @@ end local function version_info_buttonhandler(this, fields) if fields.version_check_remind then -- Erase last known, user will be reminded again at next check - cache_settings:set("update_last_known", "") + core.settings:set("update_last_known", "") this:delete() return true end if fields.version_check_never then - -- clear checked URL - core.settings:set("update_information_url", "") + core.settings:set("update_last_checked", "disabled") this:delete() return true end @@ -117,7 +116,7 @@ local function on_version_info_received(json) return end - local known_update = tonumber(cache_settings:get("update_last_known")) or 0 + local known_update = tonumber(core.settings:get("update_last_known")) or 0 -- Format: MMNNPPP (Major, Minor, Patch) local new_number = type(json.latest) == "table" and json.latest.version_code @@ -136,7 +135,7 @@ local function on_version_info_received(json) return end - cache_settings:set("update_last_known", tostring(new_number)) + core.settings:set("update_last_known", tostring(new_number)) -- Show version info dialog (once) maintab:hide() @@ -150,20 +149,20 @@ end function check_new_version() local url = core.settings:get("update_information_url") - if url == "" then + if core.settings:get("update_last_checked") == "disabled" or + url == "" then -- Never show any updates return end - -- every 2 days - if check_cache_age("update_last_checked", 2 * 24 * 3600) then + local time_now = os.time() + local time_checked = tonumber(core.settings:get("update_last_checked")) or 0 + if time_now - time_checked < 2 * 24 * 3600 then + -- Check interval of 2 entire days return end - cache_settings:set("update_last_checked", tostring(os.time())) - -- Clean old leftovers (this can be removed after 5.9.0 or so) - core.settings:remove("update_last_checked") - core.settings:remove("update_last_known") + core.settings:set("update_last_checked", tostring(time_now)) core.handle_async(function(params) local http = core.get_http_api() diff --git a/builtin/mainmenu/game_theme.lua b/builtin/mainmenu/old_code/game_theme.lua similarity index 100% rename from builtin/mainmenu/game_theme.lua rename to builtin/mainmenu/old_code/game_theme.lua diff --git a/builtin/mainmenu/old_code/init.lua b/builtin/mainmenu/old_code/init.lua new file mode 100644 index 000000000000..388ab458d04b --- /dev/null +++ b/builtin/mainmenu/old_code/init.lua @@ -0,0 +1,131 @@ +--Minetest +--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" + +local menupath = core.get_mainmenu_path() +local basepath = core.get_builtin_path() +defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" .. + DIR_DELIM .. "pack" .. DIR_DELIM + +dofile(menupath .. DIR_DELIM .. "misc.lua") + +dofile(basepath .. "common" .. DIR_DELIM .. "filterlist.lua") +dofile(basepath .. "fstk" .. DIR_DELIM .. "buttonbar.lua") +dofile(basepath .. "fstk" .. DIR_DELIM .. "dialog.lua") +dofile(basepath .. "fstk" .. DIR_DELIM .. "tabview.lua") +dofile(basepath .. "fstk" .. DIR_DELIM .. "ui.lua") +dofile(menupath .. DIR_DELIM .. "async_event.lua") +dofile(menupath .. DIR_DELIM .. "common.lua") +dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua") +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(menupath .. DIR_DELIM .. "dlg_create_world.lua") +dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua") +dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua") +dofile(menupath .. DIR_DELIM .. "dlg_register.lua") +dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua") +dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua") +dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua") + +local tabs = { + content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"), + about = dofile(menupath .. DIR_DELIM .. "tab_about.lua"), + local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua"), + play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua") +} + +-------------------------------------------------------------------------------- +local function main_event_handler(tabview, event) + if event == "MenuQuit" then + core.close() + end + return true +end + +-------------------------------------------------------------------------------- +local function init_globals() + -- Init gamedata + gamedata.worldindex = 0 + + menudata.worldlist = filterlist.create( + core.get_worlds, + compare_worlds, + -- Unique id comparison function + function(element, uid) + return element.name == uid + end, + -- Filter function + function(element, gameid) + return element.gameid == gameid + end + ) + + menudata.worldlist:add_sort_mechanism("alphabetic", sort_worlds_alphabetic) + menudata.worldlist:set_sortmode("alphabetic") + + mm_game_theme.init() + mm_game_theme.set_engine() -- This is just a fallback. + + -- Create main tabview + local tv_main = tabview_create("maintab", {x = 15.5, y = 7.1}, {x = 0, y = 0}) + + tv_main:set_autosave_tab(true) + tv_main:add(tabs.local_game) + tv_main:add(tabs.play_online) + tv_main:add(tabs.content) + tv_main:add(tabs.about) + + tv_main:set_global_event_handler(main_event_handler) + tv_main:set_fixed_size(false) + + local last_tab = core.settings:get("maintab_LAST") + if last_tab and tv_main.current_tab ~= last_tab then + tv_main:set_tab(last_tab) + end + + tv_main:set_end_button({ + icon = defaulttexturedir .. "settings_btn.png", + label = fgettext("Settings"), + name = "open_settings", + on_click = function(tabview) + local dlg = create_settings_dlg() + dlg:set_parent(tabview) + tabview:hide() + dlg:show() + return true + end, + }) + + ui.set_default("maintab") + tv_main:show() + ui.update() + + check_reinstall_mtg() + check_new_version() +end + +init_globals() diff --git a/builtin/mainmenu/old_code/misc.lua b/builtin/mainmenu/old_code/misc.lua new file mode 100644 index 000000000000..0677e96a312c --- /dev/null +++ b/builtin/mainmenu/old_code/misc.lua @@ -0,0 +1,6 @@ + +-- old non-method sound function + +function core.sound_stop(handle, ...) + return handle:stop(...) +end diff --git a/builtin/mainmenu/serverlistmgr.lua b/builtin/mainmenu/old_code/serverlistmgr.lua similarity index 83% rename from builtin/mainmenu/serverlistmgr.lua rename to builtin/mainmenu/old_code/serverlistmgr.lua index 601b42110039..ab686ded08b5 100644 --- a/builtin/mainmenu/serverlistmgr.lua +++ b/builtin/mainmenu/old_code/serverlistmgr.lua @@ -17,7 +17,7 @@ serverlistmgr = { -- continent code we detected for ourselves - my_continent = nil, + my_continent = core.get_once("continent"), -- list of locally favorites servers favorites = nil, @@ -26,15 +26,6 @@ serverlistmgr = { servers = nil, } -do - if check_cache_age("geoip_last_checked", 3600) then - local tmp = cache_settings:get("geoip") or "" - if tmp:match("^[A-Z][A-Z]$") then - serverlistmgr.my_continent = tmp - end - end -end - -------------------------------------------------------------------------------- -- Efficient data structure for normalizing arbitrary scores attached to objects -- e.g. {{"a", 3.14}, {"b", 3.14}, {"c", 20}, {"d", 0}} @@ -80,8 +71,7 @@ local WEIGHT_SORT = 2 -- how much the estimated latency contributes to the final ranking local WEIGHT_LATENCY = 1 ---- @param list of servers, will be modified. -local function order_server_list_internal(list) +local function order_server_list(list) -- calculate the scores local s1 = Normalizer:new() local s2 = Normalizer:new() @@ -100,58 +90,28 @@ local function order_server_list_internal(list) s1 = s1:calc() s2 = s2:calc() - -- pre-calculate ordering - local order = {} - for _, fav in ipairs(list) do - order[fav] = s1[fav] * WEIGHT_SORT + s2[fav] * WEIGHT_LATENCY + -- make a shallow copy and pre-calculate ordering + local res, order = {}, {} + for i = 1, #list do + local fav = list[i] + res[i] = fav + + local n = s1[fav] * WEIGHT_SORT + s2[fav] * WEIGHT_LATENCY + order[fav] = n end -- now sort the list - table.sort(list, function(fav1, fav2) + table.sort(res, function(fav1, fav2) return order[fav1] > order[fav2] end) -end - -local function order_server_list(list) - -- split the list into two parts and sort them separately, to keep empty - -- servers at the bottom. - local nonempty, empty = {}, {} - - for _, fav in ipairs(list) do - if (fav.clients or 0) > 0 then - table.insert(nonempty, fav) - else - table.insert(empty, fav) - end - end - order_server_list_internal(nonempty) - order_server_list_internal(empty) - - table.insert_all(nonempty, empty) - return nonempty + return res end local public_downloading = false local geoip_downloading = false -------------------------------------------------------------------------------- -local function fetch_geoip() - local http = core.get_http_api() - local url = core.settings:get("serverlist_url") .. "/geoip" - - local response = http.fetch_sync({ url = url }) - if not response.succeeded then - return - end - - local retval = core.parse_json(response.data) - if type(retval) ~= "table" then - return - end - return type(retval.continent) == "string" and retval.continent -end - function serverlistmgr.sync() if not serverlistmgr.servers then serverlistmgr.servers = {{ @@ -169,23 +129,37 @@ function serverlistmgr.sync() return end + -- only fetched once per MT instance if not serverlistmgr.my_continent and not geoip_downloading then geoip_downloading = true - core.handle_async(fetch_geoip, nil, function(result) - geoip_downloading = false - if not result then - return - end - serverlistmgr.my_continent = result - cache_settings:set("geoip", result) - cache_settings:set("geoip_last_checked", tostring(os.time())) - - -- re-sort list if applicable - if serverlistmgr.servers then - serverlistmgr.servers = order_server_list(serverlistmgr.servers) - core.event_handler("Refresh") + core.handle_async( + function(param) + local http = core.get_http_api() + local url = core.settings:get("serverlist_url") .. "/geoip" + + local response = http.fetch_sync({ url = url }) + if not response.succeeded then + return + end + + local retval = core.parse_json(response.data) + return retval and type(retval.continent) == "string" and retval.continent + end, + nil, + function(result) + geoip_downloading = false + if not result then + return + end + serverlistmgr.my_continent = result + core.set_once("continent", result) + -- reorder list if we already have it + if serverlistmgr.servers then + serverlistmgr.servers = order_server_list(serverlistmgr.servers) + core.event_handler("Refresh") + end end - end) + ) end if public_downloading then @@ -193,7 +167,6 @@ function serverlistmgr.sync() end public_downloading = true - -- note: this isn't cached because it's way too dynamic core.handle_async( function(param) local http = core.get_http_api() diff --git a/builtin/mainmenu/tab_about.lua b/builtin/mainmenu/old_code/tab_about.lua similarity index 98% rename from builtin/mainmenu/tab_about.lua rename to builtin/mainmenu/old_code/tab_about.lua index 1805acc8112d..8c5573399868 100644 --- a/builtin/mainmenu/tab_about.lua +++ b/builtin/mainmenu/old_code/tab_about.lua @@ -158,7 +158,7 @@ return { "style[label_button;border=false]" .. "button[0.1,3.4;5.3,0.5;label_button;" .. core.formspec_escape(version.project .. " " .. version.string) .. "]" .. - "button_url[1.5,4.1;2.5,0.8;homepage;minetest.net;https://www.minetest.net/]" .. + "button[1.5,4.1;2.5,0.8;homepage;minetest.net]" .. "hypertext[5.5,0.25;9.75,6.6;credits;" .. minetest.formspec_escape(hypertext) .. "]" -- Render information @@ -188,6 +188,10 @@ return { end, cbf_button_handler = function(this, fields, name, tabdata) + if fields.homepage then + core.open_url("https://www.minetest.net") + end + if fields.share_debug then local path = core.get_user_path() .. DIR_DELIM .. "debug.txt" core.share_file(path) diff --git a/builtin/mainmenu/tab_content.lua b/builtin/mainmenu/old_code/tab_content.lua similarity index 93% rename from builtin/mainmenu/tab_content.lua rename to builtin/mainmenu/old_code/tab_content.lua index b38f12884c5e..abe127a69349 100644 --- a/builtin/mainmenu/tab_content.lua +++ b/builtin/mainmenu/old_code/tab_content.lua @@ -29,11 +29,16 @@ end local packages_raw, packages local function update_packages() - pkgmgr.load_all() + if not pkgmgr.global_mods then + pkgmgr.refresh_globals() + end + if not pkgmgr.games then + pkgmgr.update_gamelist() + end packages_raw = {} table.insert_all(packages_raw, pkgmgr.games) - table.insert_all(packages_raw, pkgmgr.texture_packs) + table.insert_all(packages_raw, pkgmgr.get_texture_packs()) table.insert_all(packages_raw, pkgmgr.global_mods:get_list()) local function get_data() @@ -109,13 +114,12 @@ local function get_formspec(tabview, name, tabdata) modscreenshot = defaulttexturedir .. "no_screenshot.png" end + local info = core.get_content_info(selected_pkg.path) local desc = fgettext("No package description available") - if selected_pkg.description and selected_pkg.description:trim() ~= "" then - desc = core.formspec_escape(selected_pkg.description) + if info.description and info.description:trim() ~= "" then + desc = core.formspec_escape(info.description) end - local info = core.get_content_info(selected_pkg.path) - local title_and_name if selected_pkg.type == "game" then title_and_name = selected_pkg.name @@ -202,7 +206,6 @@ local function handle_doubleclick(pkg) core.settings:set("texture_path", pkg.path) end packages = nil - pkgmgr.reload_texture_packs() mm_game_theme.init() mm_game_theme.set_engine() @@ -221,7 +224,7 @@ local function handle_buttons(tabview, fields, tabname, tabdata) end if fields.btn_contentdb then - local dlg = create_contentdb_dlg() + local dlg = create_store_dlg() dlg:set_parent(tabview) tabview:hide() dlg:show() @@ -251,7 +254,7 @@ local function handle_buttons(tabview, fields, tabname, tabdata) if fields.btn_mod_mgr_update then local pkg = packages:get_list()[tabdata.selected_pkg] - local dlg = create_contentdb_dlg(nil, pkgmgr.get_contentdb_id(pkg)) + local dlg = create_store_dlg(nil, pkgmgr.get_contentdb_id(pkg)) dlg:set_parent(tabview) tabview:hide() dlg:show() @@ -267,7 +270,6 @@ local function handle_buttons(tabview, fields, tabname, tabdata) core.settings:set("texture_path", txp_path) packages = nil - pkgmgr.reload_texture_packs() mm_game_theme.init() mm_game_theme.set_engine() @@ -280,7 +282,7 @@ end return { name = "content", caption = function() - local update_count = core.settings:get_bool("contentdb_enable_updates_indicator") and update_detector.get_count() or 0 + local update_count = update_detector.get_count() if update_count == 0 then return fgettext("Content") else diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/old_code/tab_local.lua similarity index 94% rename from builtin/mainmenu/tab_local.lua rename to builtin/mainmenu/old_code/tab_local.lua index 1ed08d825db7..c35a3f6fba7a 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/old_code/tab_local.lua @@ -94,7 +94,7 @@ function singleplayer_refresh_gamebar() local btnbar = buttonbar_create( "game_button_bar", - core.settings:get_bool("enable_touch") and {x = 0, y = 7.25} or {x = 0, y = 7.475}, + TOUCHSCREEN_GUI and {x = 0, y = 7.25} or {x = 0, y = 7.475}, {x = 15.5, y = 1.25}, "#000000", game_buttonbar_button_handler) @@ -156,18 +156,10 @@ local function get_formspec(tabview, name, tabdata) -- Point the player to ContentDB when no games are found if #pkgmgr.games == 0 then - local W = tabview.width - local H = tabview.height - - local hypertext = "" .. - fgettext_ne("Minetest is a game-creation platform that allows you to play many different games.") .. "\n" .. - fgettext_ne("Minetest doesn't come with a game by default.") .. " " .. - fgettext_ne("You need to install a game before you can create a world.") - - local button_y = H * 2/3 - 0.6 return table.concat({ - "hypertext[0.375,0;", W - 2*0.375, ",", button_y, ";ht;", core.formspec_escape(hypertext), "]", - "button[5.25,", button_y, ";5,1.2;game_open_cdb;", fgettext("Install a game"), "]"}) + "style[label_button;border=false]", + "button[2.75,1.5;10,1;label_button;", fgettext("You have no games installed."), "]", + "button[5.25,3.5;5,1.2;game_open_cdb;", fgettext("Install a game"), "]"}) end local retval = "" @@ -272,7 +264,7 @@ local function main_button_handler(this, fields, name, tabdata) if fields.game_open_cdb then local maintab = ui.find_by_name("maintab") - local dlg = create_contentdb_dlg("game") + local dlg = create_store_dlg("game") dlg:set_parent(maintab) maintab:hide() dlg:show() diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/old_code/tab_online.lua similarity index 100% rename from builtin/mainmenu/tab_online.lua rename to builtin/mainmenu/old_code/tab_online.lua diff --git a/builtin/mainmenu/tests/favorites_wellformed.txt b/builtin/mainmenu/old_code/tests/favorites_wellformed.txt similarity index 100% rename from builtin/mainmenu/tests/favorites_wellformed.txt rename to builtin/mainmenu/old_code/tests/favorites_wellformed.txt diff --git a/builtin/mainmenu/tests/serverlistmgr_spec.lua b/builtin/mainmenu/old_code/tests/serverlistmgr_spec.lua similarity index 93% rename from builtin/mainmenu/tests/serverlistmgr_spec.lua rename to builtin/mainmenu/old_code/tests/serverlistmgr_spec.lua index 21ce8a226efc..25e208d100f0 100644 --- a/builtin/mainmenu/tests/serverlistmgr_spec.lua +++ b/builtin/mainmenu/old_code/tests/serverlistmgr_spec.lua @@ -1,6 +1,5 @@ -_G.core = {} +_G.core = {get_once = function(_) end} _G.unpack = table.unpack -_G.check_cache_age = function() return false end _G.serverlistmgr = {} dofile("builtin/common/vector.lua") diff --git a/builtin/mainmenu/settings/components.lua b/builtin/mainmenu/settings/components.lua index bfe64285c290..51cc0c95bb48 100644 --- a/builtin/mainmenu/settings/components.lua +++ b/builtin/mainmenu/settings/components.lua @@ -306,37 +306,18 @@ function make.flags(setting) "label[0,0.1;" .. get_label(setting) .. "]", } + local value = core.settings:get(setting.name) or setting.default 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 + for _, name in ipairs(value:split(",")) do + name = name:trim() + if name:sub(1, 2) == "no" then + checkboxes[name:sub(3)] = false + elseif name ~= "" then + checkboxes[name] = true 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 @@ -344,16 +325,18 @@ function make.flags(setting) local y = 0.55 for _, possible in ipairs(setting.possible) do - if x >= avail_w then - x = 0 - y = y + 0.5 - end + if possible:sub(1, 2) ~= "no" then + 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 + 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 end return table.concat(fs, ""), y + 0.25 @@ -372,10 +355,12 @@ function make.flags(setting) 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) + if name:sub(1, 2) ~= "no" then + if checkboxes[name] then + table.insert(values, name) + else + table.insert(values, "no" .. name) + end end end diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua index 73a72769b065..b3db6d917ad9 100644 --- a/builtin/mainmenu/settings/dlg_settings.lua +++ b/builtin/mainmenu/settings/dlg_settings.lua @@ -22,27 +22,16 @@ local component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM .. local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM .. "settings" .. DIR_DELIM .. "shadows_component.lua") -local loaded = false -local full_settings + +local full_settings = settingtypes.parse_config_file(false, true) 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") @@ -57,6 +46,49 @@ local function add_page(page) end +local change_keys = { + query_text = "Change Keys", + requires = { + keyboard_mouse = true, + }, + 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("Change Keys")), 0.8 + end, + on_submit = function(self, fields) + if fields.btn_change_keys then + core.show_keys_menu() + 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", + }, +}) + + local function load_settingtypes() local page = nil local section = nil @@ -97,134 +129,94 @@ local function load_settingtypes() end end end +load_settingtypes() +table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys) +do + local content = page_by_id.graphics_and_audio_shaders.content + local idx = table.indexof(content, "enable_dynamic_shadows") + table.insert(content, idx, shadows_component) +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 = { - keyboard_mouse = true, - }, - 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, - } - 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) - do - local content = page_by_id.graphics_and_audio_shaders.content - local idx = table.indexof(content, "enable_dynamic_shadows") - table.insert(content, idx, shadows_component) +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 - -- 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]", - } + return nil 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]", +} + + -- See if setting matches keywords local function get_setting_match_weight(entry, query_keywords) local setting_score = 0 @@ -320,12 +312,12 @@ local function check_requirements(name, requires) end local video_driver = core.get_active_driver() - local shaders_support = video_driver == "opengl" or video_driver == "opengl3" or video_driver == "ogles2" + local shaders_support = video_driver == "opengl" or video_driver == "ogles2" local special = { android = PLATFORM == "Android", desktop = PLATFORM ~= "Android", - touchscreen_gui = core.settings:get_bool("enable_touch"), - keyboard_mouse = not core.settings:get_bool("enable_touch"), + touchscreen_gui = TOUCHSCREEN_GUI, + keyboard_mouse = not TOUCHSCREEN_GUI, shaders_support = shaders_support, shaders = core.settings:get_bool("enable_shaders") and shaders_support, opengl = video_driver == "opengl", @@ -451,20 +443,19 @@ end local formspec_show_hack = false -local function get_formspec(dialogdata) +function settings_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("enable_touch") and 16.5 or 15.5, - height = core.settings:get_bool("enable_touch") and (10 - extra_h) or 12, + width = TOUCHSCREEN_GUI and 16.5 or 15.5, + height = TOUCHSCREEN_GUI and (10 - extra_h) or 12, } - local scrollbar_w = core.settings:get_bool("enable_touch") and 0.6 or 0.4 + local scrollbar_w = TOUCHSCREEN_GUI and 0.6 or 0.4 - local left_pane_width = core.settings:get_bool("enable_touch") and 4.5 or 4.25 - local left_pane_padding = 0.25 + local left_pane_width = TOUCHSCREEN_GUI and 4.5 or 4.25 local search_width = left_pane_width + scrollbar_w - (0.75 * 2) local back_w = 3 @@ -477,7 +468,7 @@ local function get_formspec(dialogdata) local fs = { "formspec_version[6]", "size[", tostring(tabsize.width), ",", tostring(tabsize.height + extra_h), "]", - core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "", + TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "", "bgcolor[#0000]", -- HACK: this is needed to allow resubmitting the same formspec @@ -525,9 +516,9 @@ local function get_formspec(dialogdata) 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") + y, left_pane_width, 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)) + :format(y, left_pane_width, other_page.id, fgettext(other_page.title)) y = y + 0.82 end @@ -650,22 +641,11 @@ local function buttonhandler(this, fields) local value = core.is_yes(fields.show_advanced) core.settings:set_bool("show_advanced", value) write_settings_early() - end - -- enable_touch is a checkbox in a setting component. We handle this - -- setting differently so we can hide/show pages using the next if-statement - if fields.enable_touch ~= nil then - local value = core.is_yes(fields.enable_touch) - core.settings:set_bool("enable_touch", value) - write_settings_early() - end - - if fields.show_advanced ~= nil or fields.enable_touch ~= nil then local suggested_page_id = update_filtered_pages(dialogdata.query) - dialogdata.components = nil - if not filtered_page_by_id[dialogdata.page_id] then + dialogdata.components = nil dialogdata.leftscroll = 0 dialogdata.rightscroll = 0 @@ -731,18 +711,12 @@ local function eventhandler(event) 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("") diff --git a/builtin/mainmenu/settings/settingtypes.lua b/builtin/mainmenu/settings/settingtypes.lua index eacd96d09386..cd988d9724c7 100644 --- a/builtin/mainmenu/settings/settingtypes.lua +++ b/builtin/mainmenu/settings/settingtypes.lua @@ -408,100 +408,5 @@ function settingtypes.parse_config_file(read_all, parse_mods) 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