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_queued.png
|
||||
textures/base/pack/cdb_update.png
|
||||
textures/base/pack/cdb_update_cropped.png
|
||||
textures/base/pack/cdb_viewonline.png
|
||||
textures/base/pack/settings_btn.png
|
||||
textures/base/pack/settings_info.png
|
||||
|
@ -154,13 +154,17 @@ end
|
||||
local function tab_header(self, size)
|
||||
local toadd = ""
|
||||
|
||||
for i=1,#self.tablist,1 do
|
||||
|
||||
for i = 1, #self.tablist do
|
||||
if toadd ~= "" then
|
||||
toadd = toadd .. ","
|
||||
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
|
||||
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)
|
||||
|
@ -74,15 +74,6 @@ local REASON_UPDATE = "update"
|
||||
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 base_url = core.settings:get("contentdb_url")
|
||||
local ret = base_url .. ("/packages/%s/releases/%d/download/"):format(
|
||||
@ -202,6 +193,10 @@ local function start_install(package, reason)
|
||||
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)
|
||||
@ -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 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(), 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 })
|
||||
if not response.succeeded then
|
||||
@ -547,6 +542,9 @@ local function install_or_update_package(this, package)
|
||||
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)
|
||||
@ -630,17 +628,17 @@ local function get_screenshot(package)
|
||||
return defaulttexturedir .. "loading_screenshot.png"
|
||||
end
|
||||
|
||||
local function fetch_pkgs(param)
|
||||
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=" .. 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
|
||||
item = item:trim()
|
||||
if item ~= "" then
|
||||
url = url .. "&hide=" .. param.urlencode(item)
|
||||
url = url .. "&hide=" .. core.urlencode(item)
|
||||
end
|
||||
end
|
||||
|
||||
@ -666,7 +664,7 @@ local function fetch_pkgs(param)
|
||||
package.id = package.id .. package.name
|
||||
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
|
||||
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
|
||||
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
|
||||
break
|
||||
end
|
||||
@ -752,7 +751,7 @@ function store.load()
|
||||
store.loading = true
|
||||
core.handle_async(
|
||||
fetch_pkgs,
|
||||
{ urlencode = urlencode },
|
||||
nil,
|
||||
function(result)
|
||||
if result then
|
||||
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
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function pkgmgr.get_texture_packs()
|
||||
local txtpath = core.get_texturepath()
|
||||
local txtpath_system = core.get_texturepath_share()
|
||||
@ -195,6 +196,23 @@ function pkgmgr.get_texture_packs()
|
||||
return retval
|
||||
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)
|
||||
local testfile = io.open(path .. DIR_DELIM .. "init.lua","r")
|
||||
@ -260,7 +278,10 @@ function pkgmgr.is_valid_modname(modpath)
|
||||
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 pkgmgr.global_mods then
|
||||
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
|
||||
local color = ""
|
||||
local icon = 0
|
||||
local error = with_error and with_error[v.virtual_path]
|
||||
local function update_error(val)
|
||||
if val and (not error or (error.type == "warning" and val.type == "error")) then
|
||||
error = val
|
||||
local icon_info = with_icon and with_icon[v.virtual_path or v.path]
|
||||
local function update_icon_info(val)
|
||||
if val and (not icon_info or (icon_info.type == "warning" and val.type == "error")) then
|
||||
icon_info = val
|
||||
end
|
||||
end
|
||||
|
||||
@ -286,8 +307,8 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
||||
|
||||
for j = 1, #rawlist do
|
||||
if rawlist[j].modpack == list[i].name then
|
||||
if with_error then
|
||||
update_error(with_error[rawlist[j].virtual_path])
|
||||
if with_icon then
|
||||
update_icon_info(with_icon[rawlist[j].virtual_path or rawlist[j].path])
|
||||
end
|
||||
|
||||
if rawlist[j].enabled then
|
||||
@ -303,10 +324,10 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
||||
color = mt_color_blue
|
||||
|
||||
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
|
||||
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
|
||||
@ -315,13 +336,17 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
||||
color = mt_color_green
|
||||
end
|
||||
|
||||
if error then
|
||||
if error.type == "warning" then
|
||||
if icon_info then
|
||||
if icon_info.type == "warning" then
|
||||
color = mt_color_orange
|
||||
icon = 2
|
||||
else
|
||||
elseif icon_info.type == "error" then
|
||||
color = mt_color_red
|
||||
icon = 3
|
||||
elseif icon_info.type == "update" then
|
||||
icon = 4
|
||||
else
|
||||
error("Unknown icon type " .. icon_info.type)
|
||||
end
|
||||
end
|
||||
|
||||
@ -332,7 +357,7 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
||||
retval[#retval + 1] = "0"
|
||||
end
|
||||
|
||||
if with_error then
|
||||
if with_icon then
|
||||
retval[#retval + 1] = icon
|
||||
end
|
||||
|
@ -57,7 +57,7 @@ local function reset()
|
||||
end
|
||||
|
||||
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()
|
||||
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(menupath .. DIR_DELIM .. "async_event.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "common.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "pkgmgr.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_contentstore.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")
|
||||
|
@ -16,6 +16,16 @@
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--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 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 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 = {
|
||||
"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;",
|
||||
pkgmgr.render_packagelist(packages, use_technical_names),
|
||||
pkgmgr.render_packagelist(packages, use_technical_names, update_icons),
|
||||
";", 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
|
||||
@ -104,15 +128,13 @@ local function get_formspec(tabview, name, tabdata)
|
||||
core.colorize("#BFBFBF", selected_pkg.name)
|
||||
end
|
||||
|
||||
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,3.1;#000]"
|
||||
})
|
||||
local desc_height = 3.2
|
||||
|
||||
if selected_pkg.is_modpack then
|
||||
desc_height = 2.1
|
||||
|
||||
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"), "]"
|
||||
})
|
||||
elseif selected_pkg.type == "mod" then
|
||||
@ -136,25 +158,39 @@ local function get_formspec(tabview, name, tabdata)
|
||||
end
|
||||
end
|
||||
elseif selected_pkg.type == "txp" then
|
||||
desc_height = 2.1
|
||||
|
||||
if selected_pkg.enabled then
|
||||
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"), "]"
|
||||
})
|
||||
else
|
||||
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"), "]"
|
||||
})
|
||||
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
|
||||
table.insert_all(retval, {
|
||||
"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
|
||||
@ -216,6 +252,16 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
|
||||
return true
|
||||
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
|
||||
local txp_path = ""
|
||||
if fields.btn_mod_mgr_use_txp then
|
||||
@ -235,7 +281,14 @@ end
|
||||
|
||||
return {
|
||||
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_button_handler = handle_buttons,
|
||||
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.
|
||||
* Possible flags: `load_client_mods`, `chat_messages`, `read_itemdefs`,
|
||||
`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
|
||||
* `minetest.debug(...)`
|
||||
|
@ -50,7 +50,7 @@ methods:
|
||||
^ tab:
|
||||
{
|
||||
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
|
||||
--TODO cbf_events = function(tabview, event, tabname), -- callback for events
|
||||
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.
|
||||
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.
|
||||
* `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
|
||||
-------
|
||||
|
@ -101,7 +101,7 @@ HTTP Requests
|
||||
* `core.download_file(url, target)` (possible in async calls)
|
||||
* `url` to download, and `target` to store to
|
||||
* returns true/false
|
||||
* `minetest.get_http_api()` (possible in async calls)
|
||||
* `core.get_http_api()` (possible in async calls)
|
||||
* returns `HTTPApiTable` containing http functions.
|
||||
* The returned table contains the functions `fetch_sync`, `fetch_async` and
|
||||
`fetch_async_get` described below.
|
||||
@ -394,10 +394,13 @@ Helpers
|
||||
* eg. `string.trim("\n \t\tfoo bar\t ")` == `"foo bar"`
|
||||
* `core.is_yes(arg)` (possible in async calls)
|
||||
* 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.
|
||||
* `minetest.decode_base64(string)` (possible in async calls)
|
||||
* `core.decode_base64(string)` (possible in async calls)
|
||||
* 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
|
||||
|
@ -651,6 +651,16 @@ int ModApiUtil::l_set_last_run_mod(lua_State *L)
|
||||
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)
|
||||
{
|
||||
API_FCT(log);
|
||||
@ -697,6 +707,8 @@ void ModApiUtil::Initialize(lua_State *L, int top)
|
||||
API_FCT(get_last_run_mod);
|
||||
API_FCT(set_last_run_mod);
|
||||
|
||||
API_FCT(urlencode);
|
||||
|
||||
LuaSettings::create(L, g_settings, g_settings_path);
|
||||
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_bytes);
|
||||
|
||||
API_FCT(urlencode);
|
||||
|
||||
LuaSettings::create(L, g_settings, g_settings_path);
|
||||
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(set_last_run_mod);
|
||||
|
||||
API_FCT(urlencode);
|
||||
|
||||
LuaSettings::create(L, g_settings, g_settings_path);
|
||||
lua_setfield(L, top, "settings");
|
||||
}
|
||||
|
@ -128,6 +128,9 @@ private:
|
||||
// set_last_run_mod(modname)
|
||||
static int l_set_last_run_mod(lua_State *L);
|
||||
|
||||
// urlencode(value)
|
||||
static int l_urlencode(lua_State *L);
|
||||
|
||||
public:
|
||||
static void Initialize(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 Width: | Height: | Size: 4.3 KiB |
Loading…
Reference in New Issue
Block a user