forked from Mirrorlandia_minetest/minetest
Add package update detection on Content tab (#13807)
This commit is contained in:
parent
ddce858c34
commit
4ee32c5441
@ -61,6 +61,7 @@ Zughy:
|
|||||||
textures/base/pack/cdb_downloading.png
|
textures/base/pack/cdb_downloading.png
|
||||||
textures/base/pack/cdb_queued.png
|
textures/base/pack/cdb_queued.png
|
||||||
textures/base/pack/cdb_update.png
|
textures/base/pack/cdb_update.png
|
||||||
|
textures/base/pack/cdb_update_cropped.png
|
||||||
textures/base/pack/cdb_viewonline.png
|
textures/base/pack/cdb_viewonline.png
|
||||||
textures/base/pack/settings_btn.png
|
textures/base/pack/settings_btn.png
|
||||||
textures/base/pack/settings_info.png
|
textures/base/pack/settings_info.png
|
||||||
|
@ -154,13 +154,17 @@ end
|
|||||||
local function tab_header(self, size)
|
local function tab_header(self, size)
|
||||||
local toadd = ""
|
local toadd = ""
|
||||||
|
|
||||||
for i=1,#self.tablist,1 do
|
for i = 1, #self.tablist do
|
||||||
|
|
||||||
if toadd ~= "" then
|
if toadd ~= "" then
|
||||||
toadd = toadd .. ","
|
toadd = toadd .. ","
|
||||||
end
|
end
|
||||||
|
|
||||||
toadd = toadd .. self.tablist[i].caption
|
local caption = self.tablist[i].caption
|
||||||
|
if type(caption) == "function" then
|
||||||
|
caption = caption(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
toadd = toadd .. caption
|
||||||
end
|
end
|
||||||
return string.format("tabheader[%f,%f;%f,%f;%s;%s;%i;true;false]",
|
return string.format("tabheader[%f,%f;%f,%f;%s;%s;%i;true;false]",
|
||||||
self.header_x, self.header_y, size.width, size.height, self.name, toadd, self.last_tab_index)
|
self.header_x, self.header_y, size.width, size.height, self.name, toadd, self.last_tab_index)
|
||||||
|
@ -74,15 +74,6 @@ local REASON_UPDATE = "update"
|
|||||||
local REASON_DEPENDENCY = "dependency"
|
local REASON_DEPENDENCY = "dependency"
|
||||||
|
|
||||||
|
|
||||||
-- encodes for use as URL parameter or path component
|
|
||||||
local function urlencode(str)
|
|
||||||
return str:gsub("[^%a%d()._~-]", function(char)
|
|
||||||
return ("%%%02X"):format(char:byte())
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
assert(urlencode("sample text?") == "sample%20text%3F")
|
|
||||||
|
|
||||||
|
|
||||||
local function get_download_url(package, reason)
|
local function get_download_url(package, reason)
|
||||||
local base_url = core.settings:get("contentdb_url")
|
local base_url = core.settings:get("contentdb_url")
|
||||||
local ret = base_url .. ("/packages/%s/releases/%d/download/"):format(
|
local ret = base_url .. ("/packages/%s/releases/%d/download/"):format(
|
||||||
@ -202,6 +193,10 @@ local function start_install(package, reason)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function queue_download(package, reason)
|
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"))
|
local max_concurrent_downloads = tonumber(core.settings:get("contentdb_max_concurrent_downloads"))
|
||||||
if number_downloading < math.max(max_concurrent_downloads, 1) then
|
if number_downloading < math.max(max_concurrent_downloads, 1) then
|
||||||
start_install(package, reason)
|
start_install(package, reason)
|
||||||
@ -222,7 +217,7 @@ local function get_raw_dependencies(package)
|
|||||||
local url_fmt = "/api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s"
|
local url_fmt = "/api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s"
|
||||||
local version = core.get_version()
|
local version = core.get_version()
|
||||||
local base_url = core.settings:get("contentdb_url")
|
local base_url = core.settings:get("contentdb_url")
|
||||||
local url = base_url .. url_fmt:format(package.url_part, core.get_max_supp_proto(), urlencode(version.string))
|
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 })
|
local response = http.fetch_sync({ url = url })
|
||||||
if not response.succeeded then
|
if not response.succeeded then
|
||||||
@ -547,6 +542,9 @@ local function install_or_update_package(this, package)
|
|||||||
error("Unknown package type: " .. package.type)
|
error("Unknown package type: " .. package.type)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if package.queued or package.downloading then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local function on_confirm()
|
local function on_confirm()
|
||||||
local deps = get_raw_dependencies(package)
|
local deps = get_raw_dependencies(package)
|
||||||
@ -630,17 +628,17 @@ local function get_screenshot(package)
|
|||||||
return defaulttexturedir .. "loading_screenshot.png"
|
return defaulttexturedir .. "loading_screenshot.png"
|
||||||
end
|
end
|
||||||
|
|
||||||
local function fetch_pkgs(param)
|
local function fetch_pkgs()
|
||||||
local version = core.get_version()
|
local version = core.get_version()
|
||||||
local base_url = core.settings:get("contentdb_url")
|
local base_url = core.settings:get("contentdb_url")
|
||||||
local url = base_url ..
|
local url = base_url ..
|
||||||
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
|
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
|
||||||
core.get_max_supp_proto() .. "&engine_version=" .. param.urlencode(version.string)
|
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
|
||||||
|
|
||||||
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
|
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
|
||||||
item = item:trim()
|
item = item:trim()
|
||||||
if item ~= "" then
|
if item ~= "" then
|
||||||
url = url .. "&hide=" .. param.urlencode(item)
|
url = url .. "&hide=" .. core.urlencode(item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -666,7 +664,7 @@ local function fetch_pkgs(param)
|
|||||||
package.id = package.id .. package.name
|
package.id = package.id .. package.name
|
||||||
end
|
end
|
||||||
|
|
||||||
package.url_part = param.urlencode(package.author) .. "/" .. param.urlencode(package.name)
|
package.url_part = core.urlencode(package.author) .. "/" .. core.urlencode(package.name)
|
||||||
|
|
||||||
if package.aliases then
|
if package.aliases then
|
||||||
for _, alias in ipairs(package.aliases) do
|
for _, alias in ipairs(package.aliases) do
|
||||||
@ -701,7 +699,8 @@ local function resolve_auto_install_spec()
|
|||||||
|
|
||||||
for _, pkg in ipairs(store.packages_full_unordered) do
|
for _, pkg in ipairs(store.packages_full_unordered) do
|
||||||
if pkg.author == auto_install_spec.author and
|
if pkg.author == auto_install_spec.author and
|
||||||
pkg.name == auto_install_spec.name then
|
(pkg.name == auto_install_spec.name or
|
||||||
|
(pkg.type == "game" and pkg.name == auto_install_spec.name .. "_game")) then
|
||||||
resolved = pkg
|
resolved = pkg
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@ -752,7 +751,7 @@ function store.load()
|
|||||||
store.loading = true
|
store.loading = true
|
||||||
core.handle_async(
|
core.handle_async(
|
||||||
fetch_pkgs,
|
fetch_pkgs,
|
||||||
{ urlencode = urlencode },
|
nil,
|
||||||
function(result)
|
function(result)
|
||||||
if result then
|
if result then
|
||||||
store.load_ok = true
|
store.load_ok = true
|
22
builtin/mainmenu/content/init.lua
Normal file
22
builtin/mainmenu/content/init.lua
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
--Minetest
|
||||||
|
--Copyright (C) 2023 rubenwardy
|
||||||
|
--
|
||||||
|
--This program is free software; you can redistribute it and/or modify
|
||||||
|
--it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
--the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
--(at your option) any later version.
|
||||||
|
--
|
||||||
|
--This program is distributed in the hope that it will be useful,
|
||||||
|
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
--GNU Lesser General Public License for more details.
|
||||||
|
--
|
||||||
|
--You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
local path = core.get_mainmenu_path() .. DIR_DELIM .. "content"
|
||||||
|
|
||||||
|
dofile(path .. DIR_DELIM .. "pkgmgr.lua")
|
||||||
|
dofile(path .. DIR_DELIM .. "update_detector.lua")
|
||||||
|
dofile(path .. DIR_DELIM .. "dlg_contentstore.lua")
|
@ -177,6 +177,7 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
function pkgmgr.get_texture_packs()
|
function pkgmgr.get_texture_packs()
|
||||||
local txtpath = core.get_texturepath()
|
local txtpath = core.get_texturepath()
|
||||||
local txtpath_system = core.get_texturepath_share()
|
local txtpath_system = core.get_texturepath_share()
|
||||||
@ -195,6 +196,23 @@ function pkgmgr.get_texture_packs()
|
|||||||
return retval
|
return retval
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
function pkgmgr.get_all()
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
|
||||||
|
result[#result + 1] = mod
|
||||||
|
end
|
||||||
|
for _, game in pairs(pkgmgr.games) do
|
||||||
|
result[#result + 1] = game
|
||||||
|
end
|
||||||
|
for _, txp in pairs(pkgmgr.get_texture_packs()) do
|
||||||
|
result[#result + 1] = txp
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function pkgmgr.get_folder_type(path)
|
function pkgmgr.get_folder_type(path)
|
||||||
local testfile = io.open(path .. DIR_DELIM .. "init.lua","r")
|
local testfile = io.open(path .. DIR_DELIM .. "init.lua","r")
|
||||||
@ -260,7 +278,10 @@ function pkgmgr.is_valid_modname(modpath)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
--- @param render_list filterlist
|
||||||
|
--- @param use_technical_names boolean to show technical names instead of human-readable titles
|
||||||
|
--- @param with_icon table or nil, from virtual path to icon object
|
||||||
|
function pkgmgr.render_packagelist(render_list, use_technical_names, with_icon)
|
||||||
if not render_list then
|
if not render_list then
|
||||||
if not pkgmgr.global_mods then
|
if not pkgmgr.global_mods then
|
||||||
pkgmgr.refresh_globals()
|
pkgmgr.refresh_globals()
|
||||||
@ -273,10 +294,10 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
|||||||
for i, v in ipairs(list) do
|
for i, v in ipairs(list) do
|
||||||
local color = ""
|
local color = ""
|
||||||
local icon = 0
|
local icon = 0
|
||||||
local error = with_error and with_error[v.virtual_path]
|
local icon_info = with_icon and with_icon[v.virtual_path or v.path]
|
||||||
local function update_error(val)
|
local function update_icon_info(val)
|
||||||
if val and (not error or (error.type == "warning" and val.type == "error")) then
|
if val and (not icon_info or (icon_info.type == "warning" and val.type == "error")) then
|
||||||
error = val
|
icon_info = val
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -286,8 +307,8 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
|||||||
|
|
||||||
for j = 1, #rawlist do
|
for j = 1, #rawlist do
|
||||||
if rawlist[j].modpack == list[i].name then
|
if rawlist[j].modpack == list[i].name then
|
||||||
if with_error then
|
if with_icon then
|
||||||
update_error(with_error[rawlist[j].virtual_path])
|
update_icon_info(with_icon[rawlist[j].virtual_path or rawlist[j].path])
|
||||||
end
|
end
|
||||||
|
|
||||||
if rawlist[j].enabled then
|
if rawlist[j].enabled then
|
||||||
@ -303,10 +324,10 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
|||||||
color = mt_color_blue
|
color = mt_color_blue
|
||||||
|
|
||||||
local rawlist = render_list:get_raw_list()
|
local rawlist = render_list:get_raw_list()
|
||||||
if v.type == "game" and with_error then
|
if v.type == "game" and with_icon then
|
||||||
for j = 1, #rawlist do
|
for j = 1, #rawlist do
|
||||||
if rawlist[j].is_game_content then
|
if rawlist[j].is_game_content then
|
||||||
update_error(with_error[rawlist[j].virtual_path])
|
update_icon_info(with_icon[rawlist[j].virtual_path or rawlist[j].path])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -315,13 +336,17 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
|||||||
color = mt_color_green
|
color = mt_color_green
|
||||||
end
|
end
|
||||||
|
|
||||||
if error then
|
if icon_info then
|
||||||
if error.type == "warning" then
|
if icon_info.type == "warning" then
|
||||||
color = mt_color_orange
|
color = mt_color_orange
|
||||||
icon = 2
|
icon = 2
|
||||||
else
|
elseif icon_info.type == "error" then
|
||||||
color = mt_color_red
|
color = mt_color_red
|
||||||
icon = 3
|
icon = 3
|
||||||
|
elseif icon_info.type == "update" then
|
||||||
|
icon = 4
|
||||||
|
else
|
||||||
|
error("Unknown icon type " .. icon_info.type)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -332,7 +357,7 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
|||||||
retval[#retval + 1] = "0"
|
retval[#retval + 1] = "0"
|
||||||
end
|
end
|
||||||
|
|
||||||
if with_error then
|
if with_icon then
|
||||||
retval[#retval + 1] = icon
|
retval[#retval + 1] = icon
|
||||||
end
|
end
|
||||||
|
|
@ -57,7 +57,7 @@ local function reset()
|
|||||||
end
|
end
|
||||||
|
|
||||||
setfenv(loadfile("builtin/common/misc_helpers.lua"), env)()
|
setfenv(loadfile("builtin/common/misc_helpers.lua"), env)()
|
||||||
setfenv(loadfile("builtin/mainmenu/pkgmgr.lua"), env)()
|
setfenv(loadfile("builtin/mainmenu/content/pkgmgr.lua"), env)()
|
||||||
|
|
||||||
function env.pkgmgr.update_gamelist()
|
function env.pkgmgr.update_gamelist()
|
||||||
table.insert(calls, { "update_gamelist" })
|
table.insert(calls, { "update_gamelist" })
|
144
builtin/mainmenu/content/update_detector.lua
Normal file
144
builtin/mainmenu/content/update_detector.lua
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
--Minetest
|
||||||
|
--Copyright (C) 2023 rubenwardy
|
||||||
|
--
|
||||||
|
--This program is free software; you can redistribute it and/or modify
|
||||||
|
--it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
--the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
--(at your option) any later version.
|
||||||
|
--
|
||||||
|
--This program is distributed in the hope that it will be useful,
|
||||||
|
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
--GNU Lesser General Public License for more details.
|
||||||
|
--
|
||||||
|
--You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
|
||||||
|
update_detector = {}
|
||||||
|
|
||||||
|
|
||||||
|
if not core.get_http_api then
|
||||||
|
update_detector.get_all = function() return {} end
|
||||||
|
update_detector.get_count = function() return 0 end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local has_fetched = false
|
||||||
|
local latest_releases
|
||||||
|
|
||||||
|
|
||||||
|
local function fetch_latest_releases()
|
||||||
|
local version = core.get_version()
|
||||||
|
local base_url = core.settings:get("contentdb_url")
|
||||||
|
local url = base_url ..
|
||||||
|
"/api/updates/?type=mod&type=game&type=txp&protocol_version=" ..
|
||||||
|
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
|
||||||
|
local http = core.get_http_api()
|
||||||
|
local response = http.fetch_sync({ url = url })
|
||||||
|
if not response.succeeded then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
return core.parse_json(response.data)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get a table from package key (author/name) to latest release id
|
||||||
|
---
|
||||||
|
--- @param callback function that takes a single argument, table or nil
|
||||||
|
local function get_latest_releases(callback)
|
||||||
|
core.handle_async(fetch_latest_releases, nil, callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function has_packages_from_cdb()
|
||||||
|
pkgmgr.refresh_globals()
|
||||||
|
pkgmgr.update_gamelist()
|
||||||
|
|
||||||
|
for _, content in pairs(pkgmgr.get_all()) do
|
||||||
|
if content.author and content.release > 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- @returns a new table with all keys lowercase
|
||||||
|
local function lowercase_keys(tab)
|
||||||
|
local ret = {}
|
||||||
|
for key, value in pairs(tab) do
|
||||||
|
ret[key:lower()] = value
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function fetch()
|
||||||
|
if has_fetched or not has_packages_from_cdb() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
has_fetched = true
|
||||||
|
|
||||||
|
get_latest_releases(function(releases)
|
||||||
|
if not releases then
|
||||||
|
has_fetched = false
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
latest_releases = lowercase_keys(releases)
|
||||||
|
if update_detector.get_count() > 0 then
|
||||||
|
local maintab = ui.find_by_name("maintab")
|
||||||
|
if not maintab.hidden then
|
||||||
|
ui.update()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- @returns a list of content with an update available
|
||||||
|
function update_detector.get_all()
|
||||||
|
if latest_releases == nil then
|
||||||
|
fetch()
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
pkgmgr.refresh_globals()
|
||||||
|
pkgmgr.update_gamelist()
|
||||||
|
|
||||||
|
local ret = {}
|
||||||
|
local all_content = pkgmgr.get_all()
|
||||||
|
for _, content in ipairs(all_content) do
|
||||||
|
if content.author and content.release > 0 then
|
||||||
|
-- The backend will account for aliases in `latest_releases`
|
||||||
|
local id = content.author:lower() .. "/"
|
||||||
|
if content.type == "game" then
|
||||||
|
id = id .. content.id
|
||||||
|
else
|
||||||
|
id = id .. content.name
|
||||||
|
end
|
||||||
|
|
||||||
|
local latest_release = latest_releases[id]
|
||||||
|
if not latest_release and content.type == "game" then
|
||||||
|
latest_release = latest_releases[id .. "_game"]
|
||||||
|
end
|
||||||
|
|
||||||
|
if latest_release and latest_release > content.release then
|
||||||
|
ret[#ret + 1] = content
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- @return number of packages with updates available
|
||||||
|
function update_detector.get_count()
|
||||||
|
return #update_detector.get_all()
|
||||||
|
end
|
@ -37,13 +37,12 @@ dofile(basepath .. "fstk" .. DIR_DELIM .. "tabview.lua")
|
|||||||
dofile(basepath .. "fstk" .. DIR_DELIM .. "ui.lua")
|
dofile(basepath .. "fstk" .. DIR_DELIM .. "ui.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "async_event.lua")
|
dofile(menupath .. DIR_DELIM .. "async_event.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "common.lua")
|
dofile(menupath .. DIR_DELIM .. "common.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "pkgmgr.lua")
|
|
||||||
dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
|
dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "game_theme.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 .. "dlg_config_world.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
|
dofile(menupath .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_contentstore.lua")
|
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
|
dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
|
||||||
|
@ -16,6 +16,16 @@
|
|||||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
|
||||||
|
local function get_content_icons(packages_with_updates)
|
||||||
|
local ret = {}
|
||||||
|
for _, content in ipairs(packages_with_updates) do
|
||||||
|
ret[content.virtual_path or content.path] = { type = "update" }
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
local packages_raw, packages
|
local packages_raw, packages
|
||||||
|
|
||||||
local function update_packages()
|
local function update_packages()
|
||||||
@ -62,14 +72,28 @@ local function get_formspec(tabview, name, tabdata)
|
|||||||
|
|
||||||
local use_technical_names = core.settings:get_bool("show_technical_names")
|
local use_technical_names = core.settings:get_bool("show_technical_names")
|
||||||
|
|
||||||
|
local packages_with_updates = update_detector.get_all()
|
||||||
|
local update_icons = get_content_icons(packages_with_updates)
|
||||||
|
local update_count = #packages_with_updates
|
||||||
|
local contentdb_label
|
||||||
|
if update_count == 0 then
|
||||||
|
contentdb_label = fgettext("Browse online content")
|
||||||
|
else
|
||||||
|
contentdb_label = fgettext("Browse online content [$1]", update_count)
|
||||||
|
end
|
||||||
|
|
||||||
local retval = {
|
local retval = {
|
||||||
"label[0.4,0.4;", fgettext("Installed Packages:"), "]",
|
"label[0.4,0.4;", fgettext("Installed Packages:"), "]",
|
||||||
"tablecolumns[color;tree;text]",
|
"tablecolumns[color;tree;image,align=inline,width=1.5",
|
||||||
|
",tooltip=", fgettext("Update available?"),
|
||||||
|
",0=", core.formspec_escape(defaulttexturedir .. "blank.png"),
|
||||||
|
",4=", core.formspec_escape(defaulttexturedir .. "cdb_update_cropped.png"),
|
||||||
|
";text]",
|
||||||
"table[0.4,0.8;6.3,4.8;pkglist;",
|
"table[0.4,0.8;6.3,4.8;pkglist;",
|
||||||
pkgmgr.render_packagelist(packages, use_technical_names),
|
pkgmgr.render_packagelist(packages, use_technical_names, update_icons),
|
||||||
";", tabdata.selected_pkg, "]",
|
";", tabdata.selected_pkg, "]",
|
||||||
|
|
||||||
"button[0.4,5.8;6.3,0.9;btn_contentdb;", fgettext("Browse online content"), "]"
|
"button[0.4,5.8;6.3,0.9;btn_contentdb;", contentdb_label, "]"
|
||||||
}
|
}
|
||||||
|
|
||||||
local selected_pkg
|
local selected_pkg
|
||||||
@ -104,15 +128,13 @@ local function get_formspec(tabview, name, tabdata)
|
|||||||
core.colorize("#BFBFBF", selected_pkg.name)
|
core.colorize("#BFBFBF", selected_pkg.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert_all(retval, {
|
local desc_height = 3.2
|
||||||
"image[7.1,0.2;3,2;", core.formspec_escape(modscreenshot), "]",
|
|
||||||
"label[10.5,1;", core.formspec_escape(title_and_name), "]",
|
|
||||||
"box[7.1,2.4;8,3.1;#000]"
|
|
||||||
})
|
|
||||||
|
|
||||||
if selected_pkg.is_modpack then
|
if selected_pkg.is_modpack then
|
||||||
|
desc_height = 2.1
|
||||||
|
|
||||||
table.insert_all(retval, {
|
table.insert_all(retval, {
|
||||||
"button[11.1,5.8;4,0.9;btn_mod_mgr_rename_modpack;",
|
"button[7.1,4.7;8,0.9;btn_mod_mgr_rename_modpack;",
|
||||||
fgettext("Rename"), "]"
|
fgettext("Rename"), "]"
|
||||||
})
|
})
|
||||||
elseif selected_pkg.type == "mod" then
|
elseif selected_pkg.type == "mod" then
|
||||||
@ -136,25 +158,39 @@ local function get_formspec(tabview, name, tabdata)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif selected_pkg.type == "txp" then
|
elseif selected_pkg.type == "txp" then
|
||||||
|
desc_height = 2.1
|
||||||
|
|
||||||
if selected_pkg.enabled then
|
if selected_pkg.enabled then
|
||||||
table.insert_all(retval, {
|
table.insert_all(retval, {
|
||||||
"button[11.1,5.8;4,0.9;btn_mod_mgr_disable_txp;",
|
"button[7.1,4.7;8,0.9;btn_mod_mgr_disable_txp;",
|
||||||
fgettext("Disable Texture Pack"), "]"
|
fgettext("Disable Texture Pack"), "]"
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
table.insert_all(retval, {
|
table.insert_all(retval, {
|
||||||
"button[11.1,5.8;4,0.9;btn_mod_mgr_use_txp;",
|
"button[7.1,4.7;8,0.9;btn_mod_mgr_use_txp;",
|
||||||
fgettext("Use Texture Pack"), "]"
|
fgettext("Use Texture Pack"), "]"
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert_all(retval, {"textarea[7.1,2.4;8,3.1;;;", desc, "]"})
|
table.insert_all(retval, {
|
||||||
|
"image[7.1,0.2;3,2;", core.formspec_escape(modscreenshot), "]",
|
||||||
|
"label[10.5,1;", core.formspec_escape(title_and_name), "]",
|
||||||
|
"box[7.1,2.4;8,", tostring(desc_height), ";#000]",
|
||||||
|
"textarea[7.1,2.4;8,", tostring(desc_height), ";;;", desc, "]",
|
||||||
|
})
|
||||||
|
|
||||||
if core.may_modify_path(selected_pkg.path) then
|
if core.may_modify_path(selected_pkg.path) then
|
||||||
table.insert_all(retval, {
|
table.insert_all(retval, {
|
||||||
"button[7.1,5.8;4,0.9;btn_mod_mgr_delete_mod;",
|
"button[7.1,5.8;4,0.9;btn_mod_mgr_delete_mod;",
|
||||||
fgettext("Uninstall Package"), "]"
|
fgettext("Uninstall"), "]"
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
if update_icons[selected_pkg.virtual_path or selected_pkg.path] then
|
||||||
|
table.insert_all(retval, {
|
||||||
|
"button[11.1,5.8;4,0.9;btn_mod_mgr_update;",
|
||||||
|
fgettext("Update"), "]"
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -216,6 +252,16 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if fields.btn_mod_mgr_update then
|
||||||
|
local pkg = packages:get_list()[tabdata.selected_pkg]
|
||||||
|
local dlg = create_store_dlg(nil, { author = pkg.author, name = pkg.id or pkg.name })
|
||||||
|
dlg:set_parent(tabview)
|
||||||
|
tabview:hide()
|
||||||
|
dlg:show()
|
||||||
|
packages = nil
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
if fields.btn_mod_mgr_use_txp or fields.btn_mod_mgr_disable_txp then
|
if fields.btn_mod_mgr_use_txp or fields.btn_mod_mgr_disable_txp then
|
||||||
local txp_path = ""
|
local txp_path = ""
|
||||||
if fields.btn_mod_mgr_use_txp then
|
if fields.btn_mod_mgr_use_txp then
|
||||||
@ -235,7 +281,14 @@ end
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name = "content",
|
name = "content",
|
||||||
caption = fgettext("Content"),
|
caption = function()
|
||||||
|
local update_count = update_detector.get_count()
|
||||||
|
if update_count == 0 then
|
||||||
|
return fgettext("Content")
|
||||||
|
else
|
||||||
|
return fgettext("Content [$1]", update_count)
|
||||||
|
end
|
||||||
|
end,
|
||||||
cbf_formspec = get_formspec,
|
cbf_formspec = get_formspec,
|
||||||
cbf_button_handler = handle_buttons,
|
cbf_button_handler = handle_buttons,
|
||||||
on_change = on_change
|
on_change = on_change
|
||||||
|
@ -672,6 +672,9 @@ Minetest namespace reference
|
|||||||
* If a flag in this table is set to true, the feature is RESTRICTED.
|
* If a flag in this table is set to true, the feature is RESTRICTED.
|
||||||
* Possible flags: `load_client_mods`, `chat_messages`, `read_itemdefs`,
|
* Possible flags: `load_client_mods`, `chat_messages`, `read_itemdefs`,
|
||||||
`read_nodedefs`, `lookup_nodes`, `read_playerinfo`
|
`read_nodedefs`, `lookup_nodes`, `read_playerinfo`
|
||||||
|
* `minetest.urlencode(str)`: Encodes non-unreserved URI characters by a
|
||||||
|
percent sign followed by two hex digits. See
|
||||||
|
[RFC 3986, section 2.3](https://datatracker.ietf.org/doc/html/rfc3986#section-2.3).
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
* `minetest.debug(...)`
|
* `minetest.debug(...)`
|
||||||
|
@ -50,7 +50,7 @@ methods:
|
|||||||
^ tab:
|
^ tab:
|
||||||
{
|
{
|
||||||
name = "tabname", -- name of tab to create
|
name = "tabname", -- name of tab to create
|
||||||
caption = "tab caption", -- text to show for tab header
|
caption = "tab caption", -- text to show for tab header. Either a string or a function: (tabview) -> string
|
||||||
cbf_button_handler = function(tabview, fields, tabname, tabdata), -- callback for button events
|
cbf_button_handler = function(tabview, fields, tabname, tabdata), -- callback for button events
|
||||||
--TODO cbf_events = function(tabview, event, tabname), -- callback for events
|
--TODO cbf_events = function(tabview, event, tabname), -- callback for events
|
||||||
cbf_formspec = function(tabview, name, tabdata), -- get formspec
|
cbf_formspec = function(tabview, name, tabdata), -- get formspec
|
||||||
|
@ -5406,6 +5406,9 @@ Utilities
|
|||||||
use `colorspec_to_bytes` to generate raw RGBA values in a predictable way.
|
use `colorspec_to_bytes` to generate raw RGBA values in a predictable way.
|
||||||
The resulting PNG image is always 32-bit. Palettes are not supported at the moment.
|
The resulting PNG image is always 32-bit. Palettes are not supported at the moment.
|
||||||
You may use this to procedurally generate textures during server init.
|
You may use this to procedurally generate textures during server init.
|
||||||
|
* `minetest.urlencode(str)`: Encodes non-unreserved URI characters by a
|
||||||
|
percent sign followed by two hex digits. See
|
||||||
|
[RFC 3986, section 2.3](https://datatracker.ietf.org/doc/html/rfc3986#section-2.3).
|
||||||
|
|
||||||
Logging
|
Logging
|
||||||
-------
|
-------
|
||||||
|
@ -101,7 +101,7 @@ HTTP Requests
|
|||||||
* `core.download_file(url, target)` (possible in async calls)
|
* `core.download_file(url, target)` (possible in async calls)
|
||||||
* `url` to download, and `target` to store to
|
* `url` to download, and `target` to store to
|
||||||
* returns true/false
|
* returns true/false
|
||||||
* `minetest.get_http_api()` (possible in async calls)
|
* `core.get_http_api()` (possible in async calls)
|
||||||
* returns `HTTPApiTable` containing http functions.
|
* returns `HTTPApiTable` containing http functions.
|
||||||
* The returned table contains the functions `fetch_sync`, `fetch_async` and
|
* The returned table contains the functions `fetch_sync`, `fetch_async` and
|
||||||
`fetch_async_get` described below.
|
`fetch_async_get` described below.
|
||||||
@ -394,10 +394,13 @@ Helpers
|
|||||||
* eg. `string.trim("\n \t\tfoo bar\t ")` == `"foo bar"`
|
* eg. `string.trim("\n \t\tfoo bar\t ")` == `"foo bar"`
|
||||||
* `core.is_yes(arg)` (possible in async calls)
|
* `core.is_yes(arg)` (possible in async calls)
|
||||||
* returns whether `arg` can be interpreted as yes
|
* returns whether `arg` can be interpreted as yes
|
||||||
* `minetest.encode_base64(string)` (possible in async calls)
|
* `core.encode_base64(string)` (possible in async calls)
|
||||||
* Encodes a string in base64.
|
* Encodes a string in base64.
|
||||||
* `minetest.decode_base64(string)` (possible in async calls)
|
* `core.decode_base64(string)` (possible in async calls)
|
||||||
* Decodes a string encoded in base64.
|
* Decodes a string encoded in base64.
|
||||||
|
* `core.urlencode(str)`: Encodes non-unreserved URI characters by a
|
||||||
|
percent sign followed by two hex digits. See
|
||||||
|
[RFC 3986, section 2.3](https://datatracker.ietf.org/doc/html/rfc3986#section-2.3).
|
||||||
|
|
||||||
|
|
||||||
Async
|
Async
|
||||||
|
@ -651,6 +651,16 @@ int ModApiUtil::l_set_last_run_mod(lua_State *L)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// urlencode(value)
|
||||||
|
int ModApiUtil::l_urlencode(lua_State *L)
|
||||||
|
{
|
||||||
|
NO_MAP_LOCK_REQUIRED;
|
||||||
|
|
||||||
|
const char *value = luaL_checkstring(L, 1);
|
||||||
|
lua_pushstring(L, urlencode(value).c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
void ModApiUtil::Initialize(lua_State *L, int top)
|
void ModApiUtil::Initialize(lua_State *L, int top)
|
||||||
{
|
{
|
||||||
API_FCT(log);
|
API_FCT(log);
|
||||||
@ -697,6 +707,8 @@ void ModApiUtil::Initialize(lua_State *L, int top)
|
|||||||
API_FCT(get_last_run_mod);
|
API_FCT(get_last_run_mod);
|
||||||
API_FCT(set_last_run_mod);
|
API_FCT(set_last_run_mod);
|
||||||
|
|
||||||
|
API_FCT(urlencode);
|
||||||
|
|
||||||
LuaSettings::create(L, g_settings, g_settings_path);
|
LuaSettings::create(L, g_settings, g_settings_path);
|
||||||
lua_setfield(L, top, "settings");
|
lua_setfield(L, top, "settings");
|
||||||
}
|
}
|
||||||
@ -723,6 +735,8 @@ void ModApiUtil::InitializeClient(lua_State *L, int top)
|
|||||||
API_FCT(colorspec_to_colorstring);
|
API_FCT(colorspec_to_colorstring);
|
||||||
API_FCT(colorspec_to_bytes);
|
API_FCT(colorspec_to_bytes);
|
||||||
|
|
||||||
|
API_FCT(urlencode);
|
||||||
|
|
||||||
LuaSettings::create(L, g_settings, g_settings_path);
|
LuaSettings::create(L, g_settings, g_settings_path);
|
||||||
lua_setfield(L, top, "settings");
|
lua_setfield(L, top, "settings");
|
||||||
}
|
}
|
||||||
@ -766,6 +780,8 @@ void ModApiUtil::InitializeAsync(lua_State *L, int top)
|
|||||||
API_FCT(get_last_run_mod);
|
API_FCT(get_last_run_mod);
|
||||||
API_FCT(set_last_run_mod);
|
API_FCT(set_last_run_mod);
|
||||||
|
|
||||||
|
API_FCT(urlencode);
|
||||||
|
|
||||||
LuaSettings::create(L, g_settings, g_settings_path);
|
LuaSettings::create(L, g_settings, g_settings_path);
|
||||||
lua_setfield(L, top, "settings");
|
lua_setfield(L, top, "settings");
|
||||||
}
|
}
|
||||||
|
@ -128,6 +128,9 @@ private:
|
|||||||
// set_last_run_mod(modname)
|
// set_last_run_mod(modname)
|
||||||
static int l_set_last_run_mod(lua_State *L);
|
static int l_set_last_run_mod(lua_State *L);
|
||||||
|
|
||||||
|
// urlencode(value)
|
||||||
|
static int l_urlencode(lua_State *L);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void Initialize(lua_State *L, int top);
|
static void Initialize(lua_State *L, int top);
|
||||||
static void InitializeAsync(lua_State *L, int top);
|
static void InitializeAsync(lua_State *L, int top);
|
||||||
|
BIN
textures/base/pack/cdb_update_cropped.png
Normal file
BIN
textures/base/pack/cdb_update_cropped.png
Normal file
Binary file not shown.
After (image error) Size: 4.3 KiB |
Loading…
Reference in New Issue
Block a user