Update mainmenu/init.lua

This commit is contained in:
Signal 2025-06-05 17:08:46 +00:00
parent 8e71b514e4
commit 67d5e435db

View file

@ -22,59 +22,46 @@ state = {}
local fe = minetest.formspec_escape
local hte = minetest.hypertext_escape
--FIXME: Replace with /content because pause menu
dofile(minetest.get_builtin_path().."mainmenu/settings/settingtypes.lua")
---[[
minetest.set_formspec_prepend("\
style[*;textcolor=#aaa]\
style_type[field;border=false]\
style_type[pwdfield;border=false]\
style_type[image_button;border=false]\
style_type[button;border=false;bgimg="..assets.."btn_bg_2.png;bgimg_middle=8,8]\
style_type[button:hovered;border=false;bgimg="..assets.."btn_bg_2_hover.png;bgimg_middle=8,8]\
style[nobg,nobg:hovered,nobg:focused,nobg:hovered+focused;border=false;bgimg="..default_textures.."blank.png;bgimg_middle=0]\
")
-- Prepended to the meta menu's formspec.
local meta_header = "formspec_version[8]\
size["..size.x..","..size.y.."]\
padding[0,0]\
style[*;textcolor=#aaa]\
style_type[image_button;border=false]\
style_type[button;border=false;bgimg="..assets.."btn_bg.png;bgimg_middle=8,8]\
bgcolor[#000;true;#151618]\
style_type[box;bordercolors=#124722;borderwidths=-5;colors=#151618]\
box[0,0;"..size.x..","..size.y..";]\
image[0,"..(size.y *0.08)..");"..size.x..","..(size.x *(72/672))..";"..default_textures.."menu_header.png]\
"
local meta_footer = "\
style[nobg;border=false;bgimg="..default_textures.."blank.png;bgimg_middle=0]\
style[show_servers_view;border=false;bgimg="..fe(assets).."menu_servers.png;bgimg_middle=0]\
style[show_servers_view:hovered;border=false;bgimg="..fe(assets).."menu_servers_hovered.png;bgimg_middle=0]\
button["..(size.x *0.05)..","..(size.y *0.3)..";4,4;show_servers_view;]\
button["..(size.x *0.05)..","..(size.y *0.3 +4)..";4,0.5;nobg;Servers]\
style[show_content_view;border=false;bgimg="..fe(assets).."menu_content.png;bgimg_middle=0]\
style[show_content_view:hovered;border=false;bgimg="..fe(assets).."menu_content_hovered.png;bgimg_middle=0]\
button["..(size.x -(size.x *0.05) -4)..","..(size.y *0.3)..";4,4;show_content_view;]\
button["..(size.x -(size.x *0.05) -4)..","..(size.y *0.3 +4)..";4,0.5;nobg;Content]\
"
-- box[0,0;"..size.x..","..size.y..";]\
-- Prepended to game menus' formspecs.
local game_header = "formspec_version[8]\
size["..size.x..","..size.y.."]\
padding[0,0]\
style[*;textcolor=#aaa]\
style_type[image_button;border=false]\
style_type[button;border=false;bgimg="..assets.."btn_bg.png;bgimg_middle=8,8]\
bgcolor[#0000;true;#0000]\
"
local servers_header = "formspec_version[8]\
size["..size.x..","..size.y.."]\
padding[0,0]\
style[*;textcolor=#aaa]\
style_type[image_button;border=false]\
style_type[field;border=false]\
style_type[pwdfield;border=false]\
style_type[button;border=false;bgimg="..assets.."btn_bg_2.png;bgimg_middle=8,8]\
style_type[button:hovered;border=false;bgimg="..assets.."btn_bg_2_hover.png;bgimg_middle=8,8]\
bgcolor[#0000;true;#151618]\
"
local content_header = "formspec_version[8]\
size["..size.x..","..size.y.."]\
padding[0,0]\
style[*;textcolor=#aaa]\
style_type[image_button;border=false]\
style_type[button;border=false;bgimg="..assets.."btn_bg.png;bgimg_middle=8,8]\
bgcolor[#0000;true;#151618]\
"
@ -194,7 +181,7 @@ local function get_worlds_for_game(id)
end
-- Returns a list of content available for download.
function get_all_content()
function get_available_content()
local version = minetest.get_version()
local cdb = minetest.settings:get("contentdb_url")
local url = cdb.."/api/packages/?type=mod&type=game&type=txp&protocol_version="..minetest.get_max_supp_proto().."&engine_version="..minetest.urlencode(version.string)
@ -232,6 +219,88 @@ function get_all_content()
return out
end
-- Returns a list of all the installed mods.
function get_all_mods(dir)
local all = false
if not dir then
if state.all_mods then return state.all_mods end
dir = minetest.get_modpath()
all = true
end
local out = {}
for _, x in ipairs(minetest.get_dir_list(dir, true)) do
local info = minetest.get_content_info(dir.."/"..x)
if info.type == "mod" then
out[#out +1] = info
end
end
if all then
state.all_mods = out
end
return out
end
-- Returns a list of all the installed games.
function get_all_games(dir)
local all = false
if not dir then
if state.all_games then return state.all_games end
dir = minetest.get_gamepath()
all = true
end
local out = {}
for _, x in ipairs(minetest.get_dir_list(dir, true)) do
local info = minetest.get_content_info(dir.."/"..x)
if info.type == "game" then
out[#out +1] = info
end
end
if all then
state.all_games = out
end
return out
end
-- Returns a list of all the installed texture packs.
function get_all_texture_packs(dir)
local all = false
if not dir then
if state.all_texture_packs then return state.all_texture_packs end
dir = minetest.get_texturepath()
all = true
end
local out = {}
for _, x in ipairs(minetest.get_dir_list(dir, true)) do
local info = minetest.get_content_info(dir.."/"..x)
if info.type == "txp" then
out[#out +1] = info
end
end
if all then
state.all_texture_packs = out
end
return out
end
-- Return a list of all content, as a conglomeration of the lists of mods, games, and texture packs.
function get_all_content()
if not state.all_content then
state.all_content = {}
table.insert_all(state.all_content, get_all_mods())
table.insert_all(state.all_content, get_all_games())
table.insert_all(state.all_content, get_all_texture_packs())
table.sort(state.all_content, function(a, b)
if not a then
return b ~= nil
elseif not b then
return a == nil
end
return a.name < b.name
end)
end
return state.all_content
end
-- Parses text as .conf format.
function parse_conf_text(txt)
local out = {}
@ -557,7 +626,7 @@ local function evaluate_template_expression(expr, vars, depth)
vars = {item = vars}
end
-- Expand all variables so we can deal with a constexpr
-- Expand all variables so we can deal with a constexpr.
local offset = 1
while offset < 100000 do
local a, b, name = expr:find("@([%a]+)", offset)
@ -709,8 +778,7 @@ function show_meta_menu(v)
"
if dist == 0 then
fs = fs.."\
style[_game_label;bgimg="..default_textures.."blank.png]\
button["..(lc -(scale /2))..","..(size.y /2 +4)..";4,0.5;_game_label;"..fe(x.title).."]\
button["..(lc -(scale /2))..","..(size.y /2 +4)..";4,0.5;nobg;"..fe(x.title).."]\
"
end
end
@ -723,7 +791,20 @@ function show_meta_menu(v)
scrollbar[0,-800;"..size.x..",0;horizontal;carousel;"..(v or "").."]\
"
minetest.update_formspec(meta_header..fs..meta_footer)
minetest.update_formspec(meta_header..fs.."\
style[show_servers_view;border=false;bgimg="..fe(assets).."menu_servers.png;bgimg_middle=0]\
style[show_servers_view:hovered;border=false;bgimg="..fe(assets).."menu_servers_hovered.png;bgimg_middle=0]\
button["..(size.x *0.05)..","..(size.y *0.3)..";4,4;show_servers_view;]\
button["..(size.x *0.05)..","..(size.y *0.3 +4)..";4,0.5;nobg;Servers]\
style[show_content_view;border=false;bgimg="..fe(assets).."menu_content.png;bgimg_middle=0]\
style[show_content_view:hovered;border=false;bgimg="..fe(assets).."menu_content_hovered.png;bgimg_middle=0]\
button["..(size.x -(size.x *0.05) -4)..","..(size.y *0.3)..";4,4;show_content_view;]\
button["..(size.x -(size.x *0.05) -4)..","..(size.y *0.3 +4)..";4,0.5;nobg;Content]\
style[show_settings_view,show_about_view;bgimg="..assets.."menu_tab_bg.png]\
style[show_settings_view:hovered,show_about_view:hovered;bgimg="..assets.."menu_tab_bg.png;bgcolor=#fffd]\
button[1,0;2,0.5;show_about_view;About]\
button["..(size.x -3)..",0;2,0.5;show_settings_view;Settings]\
")
end
-- Shows games' custom menus.
@ -823,6 +904,13 @@ function show_game_menu(args)
")
end
function add_favorite_server(address, port)
state.favorite_servers[#state.favorite_servers +1] = {address = address, port = port}
local f = io.open(minetest.get_user_path().."/client/serverlist/favoriteservers.json", "w")
f:write(minetest.write_json(state.favorite_servers))
f:flush()
f:close()
end
function refresh_server_list()
minetest.handle_async(
@ -843,17 +931,9 @@ function refresh_server_list()
end,
nil,
function(result)
state.serverlist = result
-- If we need to load the server list, we need to read the list of favorite servers as well.
local favorite_servers = minetest.parse_json(read_file(minetest.get_user_path().."/client/serverlist/favoriteservers.json") or "{}")
if #favorite_servers > 0 then
for _, x in ipairs(favorite_servers) do
x.favorite = true
end
table.insert_all(favorite_servers, state.serverlist)
state.serverlist = favorite_servers
end
state.favorite_servers = favorite_servers
local list = table.copy(state.favorite_servers)
table.insert_all(list, result)
state.serverlist = list
if state.loc == "servers" then
show_servers_menu()
end
@ -861,6 +941,94 @@ function refresh_server_list()
)
end
function search_server_list(input)
if input:trim() == "" then
return nil
end
local search_str = ""
local words = {}
local mods = {}
local games = {}
local players = {}
input = input:lower()
for x in input:gmatch("%S+") do
if x:sub(1, 4) == "mod:" then
mods[#mods +1] = x:sub(5)
elseif x:sub(1, 7) == "player:" then
players[#players +1] = x:sub(8)
elseif x:sub(1, 5) == "game:" then
games[#games +1] = x:sub(6)
else
words[#words +1] = x
end
end
-- print(dump({words = words, mods= mods, players =players, games = games}, " "))
local out = {}
for _, x in ipairs(state.serverlist) do
local passed = true
for _, a in ipairs(words) do
if not (x.description and x.description:lower():find(a, 1, true) or x.name and x.name:lower():find(a, 1, true)) then
passed = false
end
end
-- If only `continue` existed...
if passed then
if #games > 0 then
passed = false
end
for _, a in ipairs(games) do
if a == x.gameid then
passed = true
end
end
if passed then
if x.mods and #mods > 0 then
local found = 0
for _, a in ipairs(x.mods) do
for _, b in ipairs(mods) do
if a == b then
found = found +1
end
end
end
passed = found == #mods
else
passed = not not x.mods
end
if passed then
if x.clients_list and #players > 0 then
local found = 0
for _, a in ipairs(x.clients_list) do
for _, b in ipairs(players) do
if a:lower() == b then
found = found +1
end
end
end
passed = found == #players
else
passed = not not x.clients_list
end
if passed then
out[#out +1] = x
end
end
end
end
end
return out
end
-- Shows the server list.
function show_servers_menu()
local fs = ""
@ -872,23 +1040,28 @@ function show_servers_menu()
for _, x in ipairs(favorite_servers) do
x.favorite = true
end
state.favorite_servers = favorite_servers
state.serverlist = favorite_servers
-- fs = "\
-- hypertext[0,0;"..size.x..","..size.y..";ht;<global valign=middle halign=center><tag name=action color=#222 hovercolor=#444>\
-- <style size=72 color=#222><b>Fetching server list...</b></style>]\
-- "
end
-- Overlay dialogs don't occlude area tooltips, so such tooltips are disabled in that case.
local showing_dialog = state.joining_server or state.connecting_to_server or state.showing_server_mods or state.showing_server_players
local current_server
local joining_server
fs = "\
style[servers_filter;border=false;textcolor=#aaa]\
image["..(size.x *0.095)..","..(size.y *0.1 -0.85)..";"..(size.x *0.81 -2.55)..",0.75;"..assets.."btn_bg_2_light.png;8,8]\
field["..(size.x *0.095 +0.1)..","..(size.y *0.1 -0.85)..";"..(size.x *0.81 -2.2)..",0.75;servers_filter;;"..(state.menu_vars.servers_filter or "").."]\
image["..(size.x *0.095)..","..(size.y *0.1 -0.85)..";"..(size.x *0.81 -5.65)..",0.75;"..assets.."btn_bg_2_light.png;8,8]\
field["..(size.x *0.095 +0.1)..","..(size.y *0.1 -0.85)..";"..(size.x *0.81 -5.45)..",0.75;servers_filter;;"..(state.menu_vars.servers_filter or "").."]\
style[servers_search,servers_refresh,servers_cancel;bgimg="..assets.."btn_bg_2.png;bgimg_middle=8,8]\
image_button["..(size.x *0.91 -0.85)..","..(size.y *0.1 -0.85)..";0.75,0.75;"..assets.."refresh.png;servers_refresh;]\
image_button["..(size.x *0.91 -1.7)..","..(size.y *0.1 -0.85)..";0.75,0.75;"..assets.."cancel.png;servers_cancel;]\
image_button["..(size.x *0.91 -2.55)..","..(size.y *0.1 -0.85)..";0.75,0.75;"..assets.."search.png;servers_search;]\
image_button["..(size.x *0.91 -5.65)..","..(size.y *0.1 -0.85)..";0.75,0.75;"..assets.."search.png;servers_search;]\
tooltip[servers_search;Search server list;#444;#aaa]\
image_button["..(size.x *0.91 -4.8)..","..(size.y *0.1 -0.85)..";0.75,0.75;"..assets.."cancel.png;servers_cancel;]\
tooltip[servers_cancel;Cancel search;#444;#aaa]\
image_button["..(size.x *0.91 -3.95)..","..(size.y *0.1 -0.85)..";0.75,0.75;"..assets.."refresh.png;servers_refresh;]\
tooltip[servers_refresh;Refresh server list;#444;#aaa]\
button["..(size.x *0.91 -3.1)..","..(size.y *0.1 -0.85)..";3,0.75;direct_connection;Direct Connection...]\
image["..(size.x *0.095)..","..(size.y *0.1)..";"..(size.x *0.81)..","..(size.y *0.8)..";"..assets.."btn_bg.png;8,8]\
scroll_container[0,"..(size.y *0.1 +0.1)..";"..size.x..","..(size.y *0.8 -0.2)..";serverscroll;vertical;;0,0]\
"
@ -896,47 +1069,7 @@ function show_servers_menu()
local infox = state.current_server and size.x *0.6 or size.x *0.9
local max = size.x *0.9
local i = 0
-- if #state.favorite_servers > 0 then
-- fs = fs.."\
-- box["..listx..","..(i *0.5)..";"..(infox -listx)..",0.5;#"..(i %2 == 1 and "373530" or "403e39").."ff]\
-- box["..(listx -0.05)..","..(i *0.5 +0.2)..";"..(infox -listx +0.1)..",0.1;#292d2fff]\
-- box["..((infox -listx) /2 -1.5)..","..(i *0.5)..";3,0.5;#"..(i %2 == 1 and "373530" or "403e39").."ff]\
-- box["..((infox -listx) /2 -1.55)..","..(i *0.5 +0.125)..";0.1,0.25;#292d2fff]\
-- box["..((infox -listx) /2 +1.45)..","..(i *0.5 +0.125)..";0.1,0.25;#292d2fff]\
-- hypertext["..((infox -listx) /2 -1.5)..","..(i *0.5)..";3,0.5;;<global valign=middle halign=center color=#aaa>Favorite Servers]\
-- "
-- i = i +1
-- end
-- for idx, x in ipairs(state.favorite_servers) do
-- if x.address and x.port then
-- local address = minetest.encode_base64(x.address)
-- if address == state.current_server then
-- current_server = x
-- end
-- if address == state.joining_server then
-- joining_server = x
-- end
-- fs = fs.."\
-- style[serverinfof"..idx..";border=false;bgimg="..assets.."white.png;bgimg_middle=0;bgcolor=#"..(i %2 == 1 and "373530" or "403e39").."ff]\
-- style[serverinfof"..idx..":hovered;border=false;bgimg="..assets.."white.png;bgimg_middle=0]\
-- button["..listx..","..(i *0.5)..";"..(infox -listx)..",0.5;serverinfof"..idx..";]\
-- label["..(listx +0.1)..","..(i *0.5 +0.25)..";"..fe(x.address..":"..x.port).."]\
-- "
-- i = i +1
-- end
-- end
-- if i > 0 then
-- fs = fs.."\
-- box["..listx..","..(i *0.5)..";"..(infox -listx)..",0.5;#"..(i %2 == 1 and "373530" or "403e39").."ff]\
-- box["..(listx -0.05)..","..(i *0.5 +0.2)..";"..(infox -listx +0.1)..",0.1;#292d2fff]\
-- box["..((infox -listx) /2 -1.5)..","..(i *0.5)..";3,0.5;#"..(i %2 == 1 and "373530" or "403e39").."ff]\
-- box["..((infox -listx) /2 -1.55)..","..(i *0.5 +0.125)..";0.1,0.25;#292d2fff]\
-- box["..((infox -listx) /2 +1.45)..","..(i *0.5 +0.125)..";0.1,0.25;#292d2fff]\
-- hypertext["..((infox -listx) /2 -1.5)..","..(i *0.5)..";3,0.5;;<global valign=middle halign=center color=#aaa>Public Servers]\
-- "
-- i = i +1
-- end
for idx, x in ipairs(state.serverlist) do
for idx, x in ipairs(state.serverlist_filtered or state.serverlist) do
-- Skip incompatible servers. You can't join them, so showing them is rather pointless.
if (x.proto_max or version.proto_min) >= version.proto_min then
local ping_lvl = 0
@ -955,8 +1088,6 @@ function show_servers_menu()
if name == "" then
name = minetest.colorize("#888", x.address..":"..x.port)
end
-- Otherwise, the colons in an IPv6 address would make the style element blow up.
--local address = minetest.encode_base64(x.address)
local label_offset = 0.1
fs = fs.."\
style[serverinfo"..idx..";border=false;bgimg="..assets.."white.png;bgimg_middle=0;bgcolor=#"..(i %2 == 1 and "373530" or "403e39").."ff]\
@ -964,17 +1095,50 @@ function show_servers_menu()
button["..listx..","..(i *0.5)..";"..(infox -listx)..",0.5;serverinfo"..idx..";]\
"
if x.favorite then
-- The overlay dialog doesn't occlude area tooltips, so they are disabled in that case.
fs = fs.."\
image["..(listx)..","..(i *0.5)..";0.5,0.5;"..assets.."menu_servers_favorite.png]\
"..(state.joining_server and "" or "tooltip["..(listx)..","..(i *0.5)..";0.5,0.5;Favorite server;#444;#aaa]\
"..(showing_dialog and "" or "tooltip["..(listx)..","..(i *0.5)..";0.5,0.5;Favorite server;#444;#aaa]\
")
label_offset = 0.6
end
local clients = ""
local color = "#aaa"
local icons_offset = (infox -0.6)
if x.clients then
icons_offset = icons_offset -1
clients = x.clients..(x.clients_max and "/"..x.clients_max or "")
if x.clients > 0 and x.clients_max then
local percent = x.clients /x.clients_max
if percent < 0.75 then
color = "#638b67"
elseif percent < 1 then
color = "#a69174"
else
color = "#9d5b5b"
end
end
fs = fs.."\
hypertext["..icons_offset..","..(i *0.5)..";1,0.5;;<global valign=middle halign=center color="..color..">"..fe(clients).."]\
"
end
if x.pvp == false then
icons_offset = icons_offset -0.5
fs = fs.."\
image["..(icons_offset)..","..(i *0.5)..";0.5,0.5;"..assets.."menu_servers_peaceful.png]\
"..(showing_dialog and "" or "tooltip["..(icons_offset)..","..(i *0.5)..";0.5,0.5;Peaceful;#444;#aaa]\
")
end
if x.creative then
icons_offset = icons_offset -0.5
fs = fs.."\
image["..(icons_offset)..","..(i *0.5)..";0.5,0.5;"..assets.."menu_servers_creative.png]\
"..(showing_dialog and "" or "tooltip["..(icons_offset)..","..(i *0.5)..";0.5,0.5;Creative;#444;#aaa]\
")
end
fs = fs.."\
label["..(listx +label_offset)..","..(i *0.5 +0.25)..";"..fe(name).."]\
"..((x.ping or x.lag) and "image["..(infox -0.6)..","..(i *0.5)..";0.5,0.5;"..assets.."menu_servers_icon_ping_"..ping_lvl..".png]\
"..(state.joining_server and "" or "tooltip["..(infox -0.6)..","..(i *0.5)..";0.5,0.5;Ping: "..math.floor(lag)..";#444;#aaa]\
"..(showing_dialog and "" or "tooltip["..(infox -0.6)..","..(i *0.5)..";0.5,0.5;Ping: "..math.floor(lag)..";#444;#aaa]\
") or "")
i = i +1
end
@ -984,6 +1148,11 @@ function show_servers_menu()
hypertext["..listx..","..(i *0.5)..";"..(infox -listx)..",0.5;;<global valign=middle halign=center color=#777>Loading...]\
"
end
if i < 1 then
fs = fs.."\
hypertext["..listx..","..(i *0.5)..";"..(infox -listx)..",0.5;;<global valign=middle halign=center color=#777>No servers found.]\
"
end
fs = fs.."\
scroll_container_end[]\
scrollbar[-800,0;1,1;vertical;serverscroll;"..(state.menu_vars.serverscroll and tonumber(state.menu_vars.serverscroll:sub(5)) or 0).."]\
@ -996,7 +1165,31 @@ function show_servers_menu()
box["..((max -infox) *0.7 -0.05)..",0.75;0.1,0.5;#292d2fff]\
hypertext[0.2,0.8;"..((max -infox) *0.7 -0.2)..",0.5;server_address;<global valign=middle color=#aaa>"..fe(hte(state.current_server.address)).."]\
hypertext["..((max -infox) *0.7 +0.1)..",0.8;"..((max -infox) *0.3 -0.2)..",0.5;server_port;<global valign=middle color=#aaa>"..fe(hte(tostring(state.current_server.port))).."]\
hypertext[0.1,1.5;"..(max -infox -0.2)..","..((size.y *0.8 -1.5 -1.05))..";server_desc;<global color=#aaa>"..fe(hte(state.current_server.description or "")).."]\
"
local buttons_offset = 0.1
if state.current_server.mods then
fs = fs.."\
image_button["..buttons_offset..",1.5;0.5,0.5;"..assets.."menu_servers_mods.png;show_server_mods;]\
tooltip[show_server_mods;Show mod list;#444;#aaa]\
"
buttons_offset = buttons_offset +0.5
end
if state.current_server.clients then
fs = fs.."\
image_button["..buttons_offset..",1.5;0.5,0.5;"..assets.."menu_servers_players.png;show_server_players;]\
tooltip[show_server_players;Show player list;#444;#aaa]\
"
buttons_offset = buttons_offset +0.5
end
if state.current_server.favorite then
fs = fs.."\
image_button["..buttons_offset..",1.5;0.5,0.5;"..assets.."menu_servers_unfavorite.png;unfavorite_server;]\
tooltip[unfavorite_server;Remove from favorites;#444;#aaa]\
"
buttons_offset = buttons_offset +0.5
end
fs = fs.."\
hypertext[0.1,2.25;"..(max -infox -0.2)..","..((size.y *0.8 -1.5 -1.05))..";server_desc;<global color=#aaa>"..fe(hte(state.current_server.description or "")).."]\
button[0.1,"..(size.y *0.8 -1)..";"..(max -infox -0.2)..",0.75;join_server;Join Server]\
container_end[]\
"
@ -1006,28 +1199,98 @@ function show_servers_menu()
button[0,"..(size.y -1)..";1,1;to_meta_menu;<]\
"
if state.joining_server or state.connecting_to_server then
local offset = 0
if state.server_connection_error then
offset = offset +1
fs= fs.."\
hypertext[0,0;"..(size.x *0.5)..",1;;<global valign=middle color=#faa>"..fe(hte(state.server_connection_error)).."]\
if state.showing_server_mods then
fs = fs.."\
box[0,0;"..size.x..","..size.y..";#0008]\
style[_even,_even:hovered;border=false;bgimg="..assets.."white.png;bgimg_middle=0;bgcolor=#403e39ff]\
style[_odd,_odd:hovered;border=false;bgimg="..assets.."white.png;bgimg_middle=0;bgcolor=#373530ff]\
image["..(size.x /4 -0.1)..","..(size.y /4 -0.1)..";"..(size.x /2 +0.2)..","..(size.y /2 +0.2)..";"..assets.."btn_bg.png;8,8]\
scroll_container["..(size.x /4)..","..(size.y /4)..";"..(size.x /2)..","..(size.y /2 -1)..";modsscroll;vertical;;0,0]\
"
for i, x in ipairs(state.current_server.mods) do
fs = fs.."\
button[0,"..(i *0.5 -0.5)..";"..(infox -listx)..",0.5;"..(i %2 == 1 and "_odd" or "_even")..";]\
label[0.1,"..(i *0.5 -0.25)..";"..fe(x).."]\
"
end
fs = fs.."\
scroll_container_end[]\
scrollbar[-800,0;1,1;vertical;modsscroll;]\
button["..(size.x /4)..","..(size.y *0.75 -0.875)..";"..(size.x /2)..",0.75;close_dialog;Back]\
"
end
if state.showing_server_players then
fs = fs.."\
box[0,0;"..size.x..","..size.y..";#0008]\
style[_even,_even:hovered;border=false;bgimg="..assets.."white.png;bgimg_middle=0;bgcolor=#403e39ff]\
style[_odd,_odd:hovered;border=false;bgimg="..assets.."white.png;bgimg_middle=0;bgcolor=#373530ff]\
image["..(size.x /4 -0.1)..","..(size.y /4 -0.1)..";"..(size.x /2 +0.2)..","..(size.y /2 +0.2)..";"..assets.."btn_bg.png;8,8]\
scroll_container["..(size.x /4)..","..(size.y /4)..";"..(size.x /2)..","..(size.y /2 -1)..";modsscroll;vertical;;0,0]\
"
for i, x in ipairs(state.current_server.clients_list) do
fs = fs.."\
button[0,"..(i *0.5 -0.5)..";"..(infox -listx)..",0.5;"..(i %2 == 1 and "_odd" or "_even")..";]\
label[0.1,"..(i *0.5 -0.25)..";"..fe(x).."]\
"
end
fs = fs.."\
scroll_container_end[]\
scrollbar[-800,0;1,1;vertical;modsscroll;]\
button["..(size.x /4)..","..(size.y *0.75 -0.875)..";"..(size.x /2)..",0.75;close_dialog;Back]\
"
end
if state.joining_server or state.connecting_to_server then
local offset = 0
fs = fs.."\
box[0,0;"..size.x..","..size.y..";#0008]\
image["..(size.x *0.25 -0.2)..","..(size.y *0.25 -0.1)..";"..(size.x *0.5 +0.4)..","..(size.y *0.5 +0.2)..";"..assets.."btn_bg.png;8,8]\
scroll_container["..(size.x *0.25)..","..(size.y *0.25)..";"..(size.x *0.5)..","..(size.y *0.5 +0.2)..";serverconfscroll;vertical;;0,0]\
hypertext[0,"..offset..";"..(size.x *0.5)..",0.75;;<global valign=middle halign=center color=#aaa>Connecting to <b>"..fe(hte(state.current_server.name or state.current_server.address..":"..state.current_server.port)).."</b>...]\
"
if state.server_connection_error then
offset = offset +0.75
fs = fs.."\
hypertext[0,0;"..(size.x *0.5)..",0.75;;<global valign=middle color=#9d5b5b>"..fe(hte(state.server_connection_error.msg)).."]\
set_focus["..fe(state.server_connection_error.element)..";true]\
"
else
fs = fs.."\
set_focus[server_"..(state.joining_server and "username" or "address")..";true]\
"
end
if state.joining_server then
fs = fs.."\
hypertext[0,"..offset..";"..(size.x *0.5)..",0.75;;<global valign=middle halign=center color=#aaa>Connecting to <b>"..fe(hte(state.current_server.name or state.current_server.address..":"..state.current_server.port)).."</b>...]\
"
offset = offset +1
else
fs = fs.."\
set_focus[server_address;true]\
hypertext[0,"..offset..";"..(size.x *0.5)..",0.75;;<global valign=middle halign=center color=#aaa>Connecting to server...]\
"
offset = offset +1
fs = fs.."\
image[0,"..(0.25 +offset)..";"..(size.x *0.5)..",0.75;"..assets.."btn_bg_2_light.png;8,8]\
label[0.2,"..(0.1 +offset)..";Address]\
field[0.2,"..(0.25 +offset)..";"..(size.x *0.4 -0.4)..",0.75;server_address;;"..(state.menu_vars.server_address or "").."]\
box["..(size.x *0.4 -0.25)..","..(0.25 +offset)..";0.1,0.75;#292d2fff]\
label["..(size.x *0.4 -0.2)..","..(0.1 +offset)..";Port]\
field["..(size.x *0.4)..","..(0.25 +offset)..";"..(size.x *0.1 -0.2)..",0.75;server_port;;"..(state.menu_vars.server_port or "").."]\
"
offset = offset +1
end
fs = fs.."\
image[0,"..(0.25 +offset)..";"..(size.x *0.5)..",0.75;"..assets.."btn_bg_2_light.png;8,8]\
label[0.2,"..(0.1 +offset)..";Username]\
field[0.2,"..(0.25 +offset)..";"..(size.x *0.5 -0.4)..",0.75;server_username;;"..minetest.settings:get("name").."]\
image[0,"..(1.25 +offset)..";"..(size.x *0.5)..",0.75;"..assets.."btn_bg_2_light.png;8,8]\
label[0.2,"..(1.1 +offset)..";Username]\
field[0.2,"..(1.25 +offset)..";"..(size.x *0.5 -0.4)..",0.75;server_username;;"..minetest.settings:get("name").."]\
image[0,"..(2.25 +offset)..";"..(size.x *0.5)..",0.75;"..assets.."btn_bg_2_light.png;8,8]\
style[server_password;border=false;textcolor=#aaa;bgimg="..assets.."btn_bg.png]\
label[0.2,"..(2.1 +offset)..";Password]\
pwdfield[0.2,"..(2.25 +offset)..";"..(size.x *0.5 -0.4)..",0.75;server_password;]\
button[0,"..(3.25 +offset)..";"..(size.x *0.5)..",0.75;cancel_join_server;Cancel]\
button[0,"..(4.25 +offset)..";"..(size.x *0.5)..",0.75;confirm_join_server;Join]\
label[0.2,"..(1.1 +offset)..";Password]\
pwdfield[0.2,"..(1.25 +offset)..";"..(size.x *0.5 -0.4)..",0.75;server_password;]\
button[0,"..(2.25 +offset)..";"..(size.x *0.5)..",0.75;cancel_join_server;Cancel]\
button[0,"..(3.25 +offset)..";"..(size.x *0.5)..",0.75;confirm_join_server;Join]\
scroll_container_end[]\
scrollbar[-800,0;1,1;vertical;serverconfscroll;"..(state.menu_vars.serverconfscroll and tonumber(state.menu_vars.serverconfscroll:sub(5)) or 0).."]\
"
@ -1038,6 +1301,90 @@ end
-- Shows the content manager.
function show_content_menu()
local fs = ""
if not state.all_content then
get_all_content()
end
local w = size.x
local h = size.y -1.1
fs = "\
scroll_container[0,1.1;"..w..","..h..";contentscroll;horizontal;;]\
"
local cols = math.floor(w /2.5)
local spacing_x = w %2.5 /cols +0.5
local rows = math.floor(h /3)
local spacing_y = h %3 /rows +0.5
local pages = math.ceil(#state.all_content /(rows *cols))
local page = state.content_page or 1
local offset = page /10
local x = 0
local y = 0
local i = (page -1) *(rows *cols) +1
while true do
local pkg = state.all_content[i]
if not pkg then break end
local icon = pkg.type == "game" and pkg.path.."/menu/icon.png" or pkg.path.."/icon.png"
if not file_exists(icon) then
icon = assets.."menu_content.png"
end
local title = pkg.title:trim()
if title == "" then
title = pkg.name
end
fs = fs.."\
image_button["..(x *(2 +spacing_x) +(spacing_x /2) +offset)..","..(y *(2 +spacing_y) +(spacing_y /2))..";2,2;"..fe(icon)..";view_package_"..fe(pkg.path)..";]\
tooltip[view_package_"..fe(pkg.path)..";"..fe(title)..";#444;#aaa]\
hypertext["..(x *(2 +spacing_x) +(spacing_x /2) +offset)..","..(y *(2 +spacing_y) +(spacing_y /2) +2)..";2,0.5;nobg;<global color=#aaa valign=middle halign=center>"..fe(hte(title)).."]\
"
i = i +1
x = x +1
if x > cols -1 then
x = 0
y = y +1
if y > rows -1 then break end
end
end
fs = fs.."\
box[0,0;"..(pages *w)..",1;#0000]\
scroll_container_end[]\
scrollbaroptions[min=1;max="..pages..";smallstep=1]\
scrollbar[-800,0;1,1;horizontal;contentscroll;"..page.."]\
"
fs = fs.."\
box[0,0;"..size.x..",1;#403e39ff]\
box[0,1;"..size.x..",0.1;#292d2fff]\
image_button[0,0;1,1;"..assets.."search.png;test;]\
image_button[1,0;1,1;"..assets.."cancel.png;test;]\
style[test;bgimg="..assets.."white.png;bgimg_middle=0;bgcolor=#403e39ff]\
style[test:hovered;bgimg="..assets.."white.png;bgimg_middle=0]\
image[2,0.125;"..(size.x -9)..",0.75;"..assets.."btn_bg_2_light.png;8,8]\
field[2.1,0.125;"..(size.x -9.2)..",0.75;content_filter;;]\
button["..(w -7)..",0;4,1;test;Browse Online Content...]\
button["..(w -3)..",0;3,1;test;Add Content...]\
"
local start = w /2 -(pages *0.125)
for i = 1, pages do
fs = fs.."\
image_button["..(start +(i *0.25))..","..(size.y -0.5)..";0.25,0.25;"..assets..(i == page and "circle_light.png" or "circle.png")..";page"..i..";]\
"
end
if state.current_package then
end
minetest.update_formspec(content_header..fs.."\
button[0,"..(size.y -1)..";1,1;to_meta_menu;<]\
")
@ -1074,26 +1421,67 @@ function minetest.button_handler(data)
if data.servers_refresh then
refresh_server_list()
elseif data.servers_search or data.key_enter_field == "servers_filter" then
state.servers_filter = data.servers_filter
state.serverlist_filtered = search_server_list(data.servers_filter)
show_servers_menu()
elseif data.servers_cancel then
state.servers_filter = nil
state.serverlist_filtered = nil
show_servers_menu()
elseif data.show_server_mods then
state.showing_server_mods = true
show_servers_menu()
elseif data.show_server_players then
state.showing_server_players = true
show_servers_menu()
elseif data.close_dialog then
state.showing_server_players = nil
state.showing_server_mods = nil
show_servers_menu()
elseif data.direct_connection then
state.connecting_to_server = true
show_servers_menu()
elseif data.join_server then
state.joining_server = true
show_servers_menu()
elseif data.cancel_join_server then
state.joining_server = nil
state.connecting_to_server = nil
show_servers_menu()
elseif data.confirm_join_server or data.key_enter_field == "server_username" or data.key_enter_field == "server_password" then
elseif data.confirm_join_server or data.key_enter_field == "server_username" or data.key_enter_field == "server_password" or data.key_enter_field == "server_address" then
minetest.settings:set("name", data.server_username)
minetest.settings:set("address", state.current_server.address)
minetest.settings:set("remote_port", state.current_server.port)
if not tonumber(data.server_port) then
state.server_connection_error = {
msg = "Invalid port.",
element = "server_port"
}
show_servers_menu()
state.server_connection_error = nil
return
end
local address
local port
if state.connecting_to_server then
address = data.server_address:lower()
port = data.server_port:lower()
else
address = state.current_server.address:lower()
port = state.current_server.port:lower()
end
local is_favorite = false
for _, x in ipairs(state.favorite_servers) do
if x.address == address and x.port == port then
is_favorite = true
end
end
if not is_favorite then
add_favorite_server(address, port)
end
minetest.settings:set("address", address)
minetest.settings:set("remote_port", port)
gamedata = {
playername = data.server_username,
password = data.server_password,
address = state.current_server.address,
port = state.current_server.port,
address = address,
port = port,
selected_world = 0,
singleplayer = false
}
@ -1106,15 +1494,23 @@ function minetest.button_handler(data)
if idx:sub(1, 1) == "f" then
state.current_server = state.favorite_servers[tonumber(idx:sub(2))]
else
state.current_server = state.serverlist[tonumber(idx)]
state.current_server = (state.serverlist_filtered or state.serverlist)[tonumber(idx)]
end
show_servers_menu()
end
end
end
elseif state.loc == "content" then
for k, v in pairs(data) do
if data.contentscroll and data.contentscroll:sub(1, 4) == "CHG:" then
state.content_page = tonumber(data.contentscroll:sub(5))
show_content_menu()
else
for k, v in pairs(data) do
if k:sub(1, 4) == "page" then
state.content_page = tonumber(k:sub(5))
show_content_menu()
end
end
end
elseif state.loc == "game" then
for k, v in pairs(data) do
@ -1149,10 +1545,13 @@ function minetest.button_handler(data)
end
function minetest.event_handler(ev)
-- Quit on quit
-- When Esc is pressed, close the current dialog, or the game if we are on the meta menu.
if ev == "MenuQuit" then
if state.joining_server then
if state.joining_server or state.connecting_to_server or state.showing_server_mods or state.showing_server_players then
state.joining_server = nil
state.connecting_to_server = nil
state.showing_server_mods = nil
state.showing_server_players = nil
show_servers_menu()
elseif state.loc ~= "games" then
show_meta_menu()