Browse Source

Notify users to reinstall MTG if worlds exist (#13850)

Gregor Parzefall 6 months ago
parent
commit
d05da513be

+ 12 - 2
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()

+ 140 - 53
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

+ 114 - 0
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({
+		"<big>", fgettext("Minetest Game is no longer installed by default"), "</big>\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
+
+

+ 4 - 1
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()

+ 4 - 0
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