diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bec15c1b..c96b76fe8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,9 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Debug or Release" FORCE) endif() +set(ENABLE_UPDATE_CHECKER (NOT ${DEVELOPMENT_BUILD}) CACHE BOOL + "Whether to enable update checks by default") + # Included stuff set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") diff --git a/README.md b/README.md index bbd02aa94..0bc5d4b42 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,7 @@ General options and their default values: ENABLE_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp) ENABLE_SYSTEM_JSONCPP=ON - Use JsonCPP from system RUN_IN_PLACE=FALSE - Create a portable install (worlds, settings etc. in current directory) + ENABLE_UPDATE_CHECKER=TRUE - Whether to enable update checks by default USE_GPROF=FALSE - Enable profiling using GProf VERSION_EXTRA= - Text to append to version (e.g. VERSION_EXTRA=foobar -> Minetest 0.4.9-foobar) ENABLE_TOUCH=FALSE - Enable Touchscreen support (requires support by IrrlichtMt) diff --git a/android/build.gradle b/android/build.gradle index 027223601..8ba61c4a0 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -5,6 +5,7 @@ project.ext.set("versionMinor", 6) // Version Minor project.ext.set("versionPatch", 0) // Version Patch project.ext.set("versionExtra", "-dev") // Version Extra project.ext.set("versionCode", 38) // Android Version Code +project.ext.set("developmentBuild", 1) // Whether it is a development build, or a release // NOTE: +2 after each release! // +1 for ARM and +1 for ARM64 APK's, because // each APK must have a larger `versionCode` than the previous diff --git a/android/native/build.gradle b/android/native/build.gradle index 2ddc77135..2254aab3a 100644 --- a/android/native/build.gradle +++ b/android/native/build.gradle @@ -14,7 +14,8 @@ android { "versionMajor=${versionMajor}", "versionMinor=${versionMinor}", "versionPatch=${versionPatch}", - "versionExtra=${versionExtra}" + "versionExtra=${versionExtra}", + "developmentBuild=${developmentBuild}" } } } diff --git a/android/native/jni/Android.mk b/android/native/jni/Android.mk index b522042de..50651d5ba 100644 --- a/android/native/jni/Android.mk +++ b/android/native/jni/Android.mk @@ -102,6 +102,7 @@ LOCAL_CFLAGS += \ -DVERSION_MINOR=${versionMinor} \ -DVERSION_PATCH=${versionPatch} \ -DVERSION_EXTRA=${versionExtra} \ + -DDEVELOPMENT_BUILD=${developmentBuild} \ $(GPROF_DEF) ifdef USE_BUILTIN_LUA diff --git a/builtin/mainmenu/dlg_version_info.lua b/builtin/mainmenu/dlg_version_info.lua new file mode 100644 index 000000000..568fca3f4 --- /dev/null +++ b/builtin/mainmenu/dlg_version_info.lua @@ -0,0 +1,172 @@ +--[[ +Minetest +Copyright (C) 2018-2020 SmallJoker, 2022 rubenwardy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +]] + +if not core.get_http_api then + function check_new_version() + end + return +end + +local function version_info_formspec(data) + local cur_ver = core.get_version() + local title = fgettext("A new $1 version is available", cur_ver.project) + local message = + fgettext("Installed version: $1\nNew version: $2\n" .. + "Visit $3 to find out how to get the newest version and stay up to date" .. + " with features and bugfixes.", + cur_ver.string, data.new_version or "", data.url or "") + + local fs = { + "formspec_version[3]", + "size[12.8,7]", + "style_type[label;textcolor=#0E0]", + "label[0.5,0.8;", core.formspec_escape(title), "]", + "textarea[0.4,1.6;12,3.4;;;", + core.formspec_escape(message), "]", + "container[0.4,5.8]", + "button[0.0,0;4.0,0.8;version_check_visit;", fgettext("Visit website"), "]", + "button[4.5,0;3.5,0.8;version_check_remind;", fgettext("Later"), "]", + "button[8.5.5,0;3.5,0.8;version_check_never;", fgettext("Never"), "]", + "container_end[]", + } + + return table.concat(fs, "") +end + +local function version_info_buttonhandler(this, fields) + if fields.version_check_remind then + -- Erase last known, user will be reminded again at next check + core.settings:set("update_last_known", "") + this:delete() + return true + end + if fields.version_check_never then + core.settings:set("update_last_checked", "disabled") + this:delete() + return true + end + if fields.version_check_visit then + if type(this.data.url) == "string" then + core.open_url(this.data.url) + end + this:delete() + return true + end + + return false +end + +local function create_version_info_dlg(new_version, url) + assert(type(new_version) == "string") + assert(type(url) == "string") + + local retval = dialog_create("version_info", + version_info_formspec, + version_info_buttonhandler, + nil) + + retval.data.new_version = new_version + retval.data.url = url + + return retval +end + +local function get_current_version_code() + -- Format: Major.Minor.Patch + -- Convert to MMMNNNPPP + local cur_string = core.get_version().string + local cur_major, cur_minor, cur_patch = cur_string:match("^(%d+).(%d+).(%d+)") + + if not cur_patch then + core.log("error", "Failed to parse version numbers (invalid tag format?)") + return + end + + return (cur_major * 1000 + cur_minor) * 1000 + cur_patch +end + +local function on_version_info_received(json) + local maintab = ui.find_by_name("maintab") + if maintab.hidden then + -- Another dialog is open, abort. + return + end + + local known_update = tonumber(core.settings:get("update_last_known")) or 0 + + -- Format: MMNNPPP (Major, Minor, Patch) + local new_number = type(json.latest) == "table" and json.latest.version_code + if type(new_number) ~= "number" then + core.log("error", "Failed to read version number (invalid response?)") + return + end + + local cur_number = get_current_version_code() + if new_number <= known_update or new_number < cur_number then + return + end + + -- Also consider updating from 1.2.3-dev to 1.2.3 + if new_number == cur_number and not core.get_version().is_dev then + return + end + + core.settings:set("update_last_known", tostring(new_number)) + + -- Show version info dialog (once) + maintab:hide() + + local version_info_dlg = create_version_info_dlg(json.latest.version, json.latest.url) + version_info_dlg:set_parent(maintab) + version_info_dlg:show() + + ui.update() +end + +function check_new_version() + local url = core.settings:get("update_information_url") + if core.settings:get("update_last_checked") == "disabled" or + url == "" then + -- Never show any updates + return + end + + local time_now = os.time() + local time_checked = tonumber(core.settings:get("update_last_checked")) or 0 + if time_now - time_checked < 2 * 24 * 3600 then + -- Check interval of 2 entire days + return + end + + core.settings:set("update_last_checked", tostring(time_now)) + + core.handle_async(function(params) + local http = core.get_http_api() + return http.fetch_sync(params) + end, { url = url }, function(result) + local json = result.succeeded and core.parse_json(result.data) + if type(json) ~= "table" or not json.latest then + core.log("error", "Failed to read JSON output from " .. url .. + ", status code = " .. result.code) + return + end + + on_version_info_received(json) + end) +end diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index f890765fa..386d4928c 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -47,6 +47,7 @@ dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua") dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua") dofile(menupath .. DIR_DELIM .. "dlg_register.lua") dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua") +dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua") local tabs = {} @@ -121,8 +122,8 @@ local function init_globals() end ui.set_default("maintab") + check_new_version() tv_main:show() - ui.update() end diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index caa6e4db3..52b4b4d9d 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -571,6 +571,19 @@ serverlist_url (Serverlist URL) string servers.minetest.net # If disabled, new accounts will be registered automatically when logging in. enable_split_login_register (Enable split login/register) bool true +# URL to JSON file which provides information about the newest Minetest release +update_information_url (Update information URL) string https://www.minetest.net/release_info.json + +# Unix timestamp (integer) of when the client last checked for an update +# Set this value to "disabled" to never check for updates. +update_last_checked (Last update check) string + +# Version number which was last seen during an update check. +# +# Representation: MMMIIIPPP, where M=Major, I=Minor, P=Patch +# Ex: 5.5.0 is 005005000 +update_last_known (Last known version update) int 0 + [*Server] # Name of the player. diff --git a/doc/lua_api.txt b/doc/lua_api.txt index c7c78e877..9e1633a14 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4926,6 +4926,7 @@ Utilities * `string`: Simple version, eg, "1.2.3-dev" * `hash`: Full git version (only set if available), eg, "1.2.3-dev-01234567-dirty". + * `is_dev`: Boolean value indicating whether it's a development build Use this for informational purposes only. The information in the returned table does not represent the capabilities of the engine, nor is it reliable or verifiable. Compatible forks will have a different name and diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in index b1298165e..17b70e268 100644 --- a/src/cmake_config.h.in +++ b/src/cmake_config.h.in @@ -15,6 +15,8 @@ #define BUILD_TYPE "@CMAKE_BUILD_TYPE@" #define ICON_DIR "@ICONDIR@" #cmakedefine01 RUN_IN_PLACE +#cmakedefine01 DEVELOPMENT_BUILD +#cmakedefine01 ENABLE_UPDATE_CHECKER #cmakedefine01 USE_GETTEXT #cmakedefine01 USE_CURL #cmakedefine01 USE_SOUND diff --git a/src/config.h b/src/config.h index 50e118428..a4c6c9f10 100644 --- a/src/config.h +++ b/src/config.h @@ -16,6 +16,7 @@ #define PROJECT_NAME "minetest" #define PROJECT_NAME_C "Minetest" #define STATIC_SHAREDIR "" + #define ENABLE_UPDATE_CHECKER 0 #define VERSION_STRING STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_PATCH) STR(VERSION_EXTRA) #endif #ifdef NDEBUG diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 222000712..c5d92e680 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -335,6 +335,12 @@ void set_default_settings() settings->setDefault("contentdb_flag_blacklist", "nonfree, desktop_default"); #endif + settings->setDefault("update_information_url", "https://www.minetest.net/release_info.json"); +#if ENABLE_UPDATE_CHECKER + settings->setDefault("update_last_checked", ""); +#else + settings->setDefault("update_last_checked", "disabled"); +#endif // Server settings->setDefault("disable_escape_sequences", "false"); diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 5d4ca6747..1f9914e72 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -1686,7 +1686,7 @@ void GUIFormSpecMenu::parseField(parserData* data, const std::string &element, void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &element) { - MY_CHECKCLIENT("list"); + MY_CHECKCLIENT("hypertext"); std::vector parts; if (!precheckElement("hypertext", element, 4, 4, parts)) diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 47a68ad75..f602aed99 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -480,6 +480,8 @@ int ModApiUtil::l_get_version(lua_State *L) lua_setfield(L, table, "hash"); } + lua_pushboolean(L, DEVELOPMENT_BUILD); + lua_setfield(L, table, "is_dev"); return 1; } diff --git a/util/bump_version.sh b/util/bump_version.sh index 3e64bfd86..271886d68 100755 --- a/util/bump_version.sh +++ b/util/bump_version.sh @@ -26,6 +26,7 @@ perform_release() { sed -i -re "s/^set\(DEVELOPMENT_BUILD TRUE\)$/set(DEVELOPMENT_BUILD FALSE)/" CMakeLists.txt sed -i 's/project.ext.set("versionExtra", "-dev")/project.ext.set("versionExtra", "")/' android/build.gradle + sed -i 's/project.ext.set("developmentBuild", 1)/project.ext.set("developmentBuild", 0)/' android/build.gradle sed -i -re "s/\"versionCode\", [0-9]+/\"versionCode\", $NEW_ANDROID_VERSION_CODE/" android/build.gradle sed -i '/\