diff --git a/LICENSE.txt b/LICENSE.txt
index f7930f528..503dd62d2 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -14,6 +14,9 @@ https://www.apache.org/licenses/LICENSE-2.0.html
Textures by Zughy are under CC BY-SA 4.0
https://creativecommons.org/licenses/by-sa/4.0/
+Textures by cx384 are under CC BY-SA 4.0
+https://creativecommons.org/licenses/by-sa/4.0/
+
Media files by DS are under CC BY-SA 4.0
https://creativecommons.org/licenses/by-sa/4.0/
@@ -64,7 +67,13 @@ Zughy:
textures/base/pack/settings_info.png
textures/base/pack/settings_reset.png
textures/base/pack/server_url.png
+ textures/base/pack/server_url_unavailable.png
textures/base/pack/server_view_clients.png
+ textures/base/pack/server_view_clients_unavailable.png
+
+cx384:
+ textures/base/pack/server_view_mods.png
+ textures/base/pack/server_view_mods_unavailable.png
appgurueu:
textures/base/pack/server_incompatible.png
diff --git a/builtin/mainmenu/dlg_server_list_mods.lua b/builtin/mainmenu/dlg_server_list_mods.lua
new file mode 100644
index 000000000..ad44ac37f
--- /dev/null
+++ b/builtin/mainmenu/dlg_server_list_mods.lua
@@ -0,0 +1,105 @@
+-- Luanti
+-- Copyright (C) 2024 cx384
+-- SPDX-License-Identifier: LGPL-2.1-or-later
+
+local function get_formspec(dialogdata)
+ local TOUCH_GUI = core.settings:get_bool("touch_gui")
+ local server = dialogdata.server
+ local group_by_prefix = dialogdata.group_by_prefix
+ local expand_all = dialogdata.expand_all
+
+ -- A wrongly behaving server may send ill formed mod names
+ table.sort(server.mods)
+
+ local cells = {}
+ if group_by_prefix then
+ local function get_prefix(mod)
+ return mod:match("[^_]*")
+ end
+ local count = {}
+ for _, mod in ipairs(server.mods) do
+ local prefix = get_prefix(mod)
+ count[prefix] = (count[prefix] or 0) + 1
+ end
+ local last_prefix
+ local function add_row(depth, mod)
+ table.insert(cells, ("%d"):format(depth))
+ table.insert(cells, mod)
+ end
+ for i, mod in ipairs(server.mods) do
+ local prefix = get_prefix(mod)
+ if last_prefix == prefix then
+ add_row(1, mod)
+ elseif count[prefix] > 1 then
+ add_row(0, prefix)
+ add_row(1, mod)
+ else
+ add_row(0, mod)
+ end
+ last_prefix = prefix
+ end
+ else
+ cells = table.copy(server.mods)
+ end
+
+ for i, cell in ipairs(cells) do
+ cells[i] = core.formspec_escape(cell)
+ end
+ cells = table.concat(cells, ",")
+
+ local heading
+ if server.gameid then
+ heading = fgettext("The $1 server uses a game called $2 and the following mods:",
+ "" .. core.hypertext_escape(server.name) .. "",
+ "")
+ else
+ heading = fgettext("The $1 server uses the following mods:",
+ "" .. core.hypertext_escape(server.name) .. "")
+ end
+
+ local formspec = {
+ "formspec_version[8]",
+ "size[8,9.5]",
+ TOUCH_GUI and "padding[0.01,0.01]" or "",
+ "hypertext[0,0;8,1.5;;", heading, "]",
+ "tablecolumns[", group_by_prefix and
+ (expand_all and "indent;text" or "tree;text") or "text", "]",
+ "table[0.5,1.5;7,6.8;mods;", cells, "]",
+ "checkbox[0.5,8.7;group_by_prefix;", fgettext("Group by prefix"), ";",
+ group_by_prefix and "true" or "false", "]",
+ group_by_prefix and ("checkbox[0.5,9.15;expand_all;" .. fgettext("Expand all") .. ";" ..
+ (expand_all and "true" or "false") .. "]") or "",
+ "button[5.5,8.5;2,0.8;quit;OK]"
+ }
+ return table.concat(formspec, "")
+end
+
+local function buttonhandler(this, fields)
+ if fields.quit then
+ this:delete()
+ return true
+ end
+
+ if fields.group_by_prefix then
+ this.data.group_by_prefix = core.is_yes(fields.group_by_prefix)
+ return true
+ end
+
+ if fields.expand_all then
+ this.data.expand_all = core.is_yes(fields.expand_all)
+ return true
+ end
+
+ return false
+end
+
+function create_server_list_mods_dialog(server)
+ local retval = dialog_create("dlg_server_list_mods",
+ get_formspec,
+ buttonhandler,
+ nil)
+ retval.data.group_by_prefix = false
+ retval.data.expand_all = false
+ retval.data.server = server
+ return retval
+end
diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua
index 4e1c201cd..9eceb41a8 100644
--- a/builtin/mainmenu/init.lua
+++ b/builtin/mainmenu/init.lua
@@ -56,6 +56,7 @@ dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua")
dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua")
dofile(menupath .. DIR_DELIM .. "dlg_clients_list.lua")
+dofile(menupath .. DIR_DELIM .. "dlg_server_list_mods.lua")
local tabs = {
content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"),
diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua
index 02c6a9c24..bd7f45342 100644
--- a/builtin/mainmenu/tab_online.lua
+++ b/builtin/mainmenu/tab_online.lua
@@ -161,6 +161,26 @@ local function get_formspec(tabview, name, tabdata)
core.formspec_escape(gamedata.serverdescription) .. "]"
end
+ -- Mods button
+ local mods = selected_server.mods
+ if mods and #mods > 0 then
+ local tooltip = ""
+ if selected_server.gameid then
+ tooltip = fgettext("Game: $1", selected_server.gameid) .. "\n"
+ end
+ tooltip = tooltip .. fgettext("Number of mods: $1", #mods)
+
+ retval = retval ..
+ "tooltip[btn_view_mods;" .. tooltip .. "]" ..
+ "style[btn_view_mods;padding=6]" ..
+ "image_button[4,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir ..
+ "server_view_mods.png") .. ";btn_view_mods;]"
+ else
+ retval = retval .. "image[4.1,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir ..
+ "server_view_mods_unavailable.png") .. "]"
+ end
+
+ -- Clients list button
local clients_list = selected_server.clients_list
local can_view_clients_list = clients_list and #clients_list > 0
if can_view_clients_list then
@@ -178,15 +198,23 @@ local function get_formspec(tabview, name, tabdata)
retval = retval .. "style[btn_view_clients;padding=6]"
retval = retval .. "image_button[4.5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir ..
"server_view_clients.png") .. ";btn_view_clients;]"
+ else
+ retval = retval .. "image[4.6,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir ..
+ "server_view_clients_unavailable.png") .. "]"
end
+ -- URL button
if selected_server.url then
retval = retval .. "tooltip[btn_server_url;" .. fgettext("Open server website") .. "]"
retval = retval .. "style[btn_server_url;padding=6]"
- retval = retval .. "image_button[" .. (can_view_clients_list and "4" or "4.5") .. ",1.3;0.5,0.5;" ..
+ retval = retval .. "image_button[3.5,1.3;0.5,0.5;" ..
core.formspec_escape(defaulttexturedir .. "server_url.png") .. ";btn_server_url;]"
+ else
+ retval = retval .. "image[3.6,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir ..
+ "server_url_unavailable.png") .. "]"
end
+ -- Favorites toggle button
if is_selected_fav() then
retval = retval .. "tooltip[btn_delete_favorite;" .. fgettext("Remove favorite") .. "]"
retval = retval .. "style[btn_delete_favorite;padding=6]"
@@ -468,6 +496,14 @@ local function main_button_handler(tabview, fields, name, tabdata)
return true
end
+ if fields.btn_view_mods then
+ local dlg = create_server_list_mods_dialog(find_selected_server())
+ dlg:set_parent(tabview)
+ tabview:hide()
+ dlg:show()
+ return true
+ end
+
if fields.btn_mp_clear then
tabdata.search_for = ""
menudata.search_result = nil
diff --git a/textures/base/pack/server_url_unavailable.png b/textures/base/pack/server_url_unavailable.png
new file mode 100644
index 000000000..8010ad469
Binary files /dev/null and b/textures/base/pack/server_url_unavailable.png differ
diff --git a/textures/base/pack/server_view_clients_unavailable.png b/textures/base/pack/server_view_clients_unavailable.png
new file mode 100644
index 000000000..964efcde1
Binary files /dev/null and b/textures/base/pack/server_view_clients_unavailable.png differ
diff --git a/textures/base/pack/server_view_mods.png b/textures/base/pack/server_view_mods.png
new file mode 100644
index 000000000..a2611d92d
Binary files /dev/null and b/textures/base/pack/server_view_mods.png differ
diff --git a/textures/base/pack/server_view_mods_unavailable.png b/textures/base/pack/server_view_mods_unavailable.png
new file mode 100644
index 000000000..4238bb786
Binary files /dev/null and b/textures/base/pack/server_view_mods_unavailable.png differ