From d05da513beef622b8c74f0ab5c56af9c74512b2b Mon Sep 17 00:00:00 2001 From: Gregor Parzefall <82708541+grorp@users.noreply.github.com> Date: Sun, 8 Oct 2023 17:47:00 +0200 Subject: [PATCH] Notify users to reinstall MTG if worlds exist (#13850) --- builtin/fstk/dialog.lua | 14 +- builtin/mainmenu/dlg_contentstore.lua | 193 ++++++++++++++++++------- builtin/mainmenu/dlg_reinstall_mtg.lua | 114 +++++++++++++++ builtin/mainmenu/init.lua | 5 +- builtin/settingtypes.txt | 4 + 5 files changed, 274 insertions(+), 56 deletions(-) create mode 100644 builtin/mainmenu/dlg_reinstall_mtg.lua diff --git a/builtin/fstk/dialog.lua b/builtin/fstk/dialog.lua index ea57df1d2..c50d23855 100644 --- a/builtin/fstk/dialog.lua +++ b/builtin/fstk/dialog.lua @@ -38,8 +38,18 @@ local dialog_metatable = { handle_events = function(self,event) if not self.hidden then return self.eventhandler(self,event) end end, - hide = function(self) self.hidden = true end, - show = function(self) self.hidden = false end, + hide = function(self) + if not self.hidden then + self.hidden = true + self.eventhandler(self, "DialogHide") + end + end, + show = function(self) + if self.hidden then + self.hidden = false + self.eventhandler(self, "DialogShow") + end + end, delete = function(self) if self.parent ~= nil then self.parent:show() diff --git a/builtin/mainmenu/dlg_contentstore.lua b/builtin/mainmenu/dlg_contentstore.lua index 73fc5fb0b..880e3a157 100644 --- a/builtin/mainmenu/dlg_contentstore.lua +++ b/builtin/mainmenu/dlg_contentstore.lua @@ -56,6 +56,9 @@ local filter_types_titles = { fgettext("Texture packs"), } +-- Automatic package installation +local auto_install_spec = nil + local number_downloading = 0 local download_queue = {} @@ -532,6 +535,47 @@ function confirm_overwrite.create(package, callback) 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 + + + 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(".") @@ -644,6 +688,59 @@ local function sort_and_filter_pkgs() 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 resolved = nil + + for _, pkg in ipairs(store.packages_full_unordered) do + if pkg.author == auto_install_spec.author and + pkg.name == auto_install_spec.name then + resolved = pkg + break + end + end + + if not resolved then + gamedata.errormessage = fgettext("The package $1/$2 was not found.", + auto_install_spec.author, auto_install_spec.name) + 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() @@ -658,20 +755,21 @@ function store.load() { urlencode = urlencode }, 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() - store.load_ok = true - store.load_error = false + sort_and_filter_pkgs() + do_auto_install() else store.load_error = true end store.loading = false - core.event_handler("Refresh") + ui.update() end ) end @@ -725,11 +823,12 @@ end function store.sort_packages() local ret = {} + local auto_install_pkg = resolve_auto_install_spec() -- can be nil + -- Add installed content - for i=1, #store.packages_full_unordered do - local package = store.packages_full_unordered[i] - if package.path then - ret[#ret + 1] = package + for _, pkg in ipairs(store.packages_full_unordered) do + if pkg.path and pkg ~= auto_install_pkg then + ret[#ret + 1] = pkg end end @@ -747,13 +846,17 @@ function store.sort_packages() end) -- Add uninstalled content - for i=1, #store.packages_full_unordered do - local package = store.packages_full_unordered[i] - if not package.path then - ret[#ret + 1] = package + 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 @@ -1048,45 +1151,7 @@ function store.handle_submit(this, fields) assert(package) if fields["install_" .. i] then - 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 - - - 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 - + install_or_update_package(this, package) return true end @@ -1110,7 +1175,24 @@ function store.handle_submit(this, fields) return false end -function create_store_dlg(type) +function store.handle_events(event) + if event == "DialogShow" then + -- 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 +--- Package specification of the form { author = string, name = string }. +--- Sets package to install or update automatically. +function create_store_dlg(type, install_spec) search_string = "" cur_page = 1 if type then @@ -1125,10 +1207,15 @@ function create_store_dlg(type) 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, - nil) + store.handle_events) end diff --git a/builtin/mainmenu/dlg_reinstall_mtg.lua b/builtin/mainmenu/dlg_reinstall_mtg.lua new file mode 100644 index 000000000..26af19708 --- /dev/null +++ b/builtin/mainmenu/dlg_reinstall_mtg.lua @@ -0,0 +1,114 @@ +--Minetest +--Copyright (C) 2023 Gregor Parzefall +-- +--This program is free software; you can redistribute it and/or modify +--it under the terms of the GNU Lesser General Public License as published by +--the Free Software Foundation; either version 2.1 of the License, or +--(at your option) any later version. +-- +--This program is distributed in the hope that it will be useful, +--but WITHOUT ANY WARRANTY; without even the implied warranty of +--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +--GNU Lesser General Public License for more details. +-- +--You should have received a copy of the GNU Lesser General Public License along +--with this program; if not, write to the Free Software Foundation, Inc., +--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +function check_reinstall_mtg() + 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 + core.settings:set_bool("no_mtg_notification", true) + return + end + end + + local mtg_world_found = false + local worlds = core.get_worlds() + for _, world in ipairs(worlds) do + if world.gameid == "minetest" then + mtg_world_found = true + break + end + end + if not mtg_world_found then + core.settings:set_bool("no_mtg_notification", true) + return + end + + mm_game_theme.reset() + + local maintab = ui.find_by_name("maintab") + + local dlg = create_reinstall_mtg_dlg() + dlg:set_parent(maintab) + maintab:hide() + dlg:show() + ui.update() +end + +local function get_formspec(dialogdata) + local markup = table.concat({ + "", fgettext("Minetest Game is no longer installed by default"), "\n", + fgettext("For a long time, the Minetest engine shipped with a default game called \"Minetest Game\". " .. + "Since Minetest 5.8.0, Minetest ships without a default game."), "\n", + fgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."), + }) + + return table.concat({ + "formspec_version[6]", + "size[12.8,7]", + "hypertext[0.375,0.375;12.05,5.2;text;", minetest.formspec_escape(markup), "]", + "container[0.375,5.825]", + "style[dismiss;bgcolor=red]", + "button[0,0;4,0.8;dismiss;", fgettext("Dismiss"), "]", + "button[4.25,0;8,0.8;reinstall;", fgettext("Reinstall Minetest Game"), "]", + "container_end[]", + }) +end + +local function buttonhandler(this, fields) + if fields.reinstall then + -- Don't set "no_mtg_notification" here so that the dialog will be shown + -- again if downloading MTG fails for whatever reason. + this:delete() + + local maintab = ui.find_by_name("maintab") + + local dlg = create_store_dlg(nil, { author = "Minetest", name = "minetest_game" }) + dlg:set_parent(maintab) + maintab:hide() + dlg:show() + + return true + end + + if fields.dismiss then + core.settings:set_bool("no_mtg_notification", true) + this:delete() + return true + end +end + +local function eventhandler(event) + if event == "MenuQuit" then + -- Don't allow closing the dialog with ESC, but still allow exiting + -- Minetest. + core.close() + return true + end + return false +end + +function create_reinstall_mtg_dlg() + local dlg = dialog_create("dlg_reinstall_mtg", get_formspec, + buttonhandler, eventhandler) + return dlg +end + + diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 8b544a638..8d2bb2693 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -50,6 +50,7 @@ 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"), @@ -121,9 +122,11 @@ local function init_globals() }) ui.set_default("maintab") - check_new_version() tv_main:show() ui.update() + + check_reinstall_mtg() + check_new_version() end init_globals() diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 71a2d327d..80f1565bf 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -2250,6 +2250,10 @@ update_last_checked (Last update check) string # Ex: 5.5.0 is 005005000 update_last_known (Last known version update) int 0 +# If this is set to true, the user will never (again) be shown the +# "reinstall Minetest Game" notification. +no_mtg_notification (Don't show "reinstall Minetest Game" notification) bool false + # Key for moving the player forward. keymap_forward (Forward key) key KEY_KEY_W