Update mainmenu/init.lua

This commit is contained in:
Signal 2025-06-28 15:16:16 +00:00
parent a577b070e9
commit e4bea7c082

View file

@ -12,18 +12,17 @@ size = window.max_formspec_size
default_textures = minetest.get_texturepath_share().."/base/pack/" default_textures = minetest.get_texturepath_share().."/base/pack/"
--FIXME: Assets --FIXME: Assets
local assets = "/Users/iboettcher/eclipse-workspace/mods/mtmenu/" local assets = os.getenv("HOME").."/eclipse-workspace/mods/mtmenu/"
local version = minetest.get_version() local version = minetest.get_version()
-- Holds all the interface parts -- This is where all view-specific state information goes.
state = {} state = {}
local fe = minetest.formspec_escape local fe = minetest.formspec_escape
local hte = minetest.hypertext_escape local hte = minetest.hypertext_escape
--FIXME: Replace with /content because pause menu dofile(minetest.get_builtin_path().."common/settings/settingtypes.lua")
dofile(minetest.get_builtin_path().."mainmenu/settings/settingtypes.lua")
---[[ ---[[
@ -37,7 +36,6 @@ minetest.set_formspec_prepend("\
style[nobg,nobg:hovered,nobg:focused,nobg:hovered+focused;border=false;bgimg="..default_textures.."blank.png;bgimg_middle=0]\ 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]\ local meta_header = "formspec_version[8]\
size["..size.x..","..size.y.."]\ size["..size.x..","..size.y.."]\
padding[0,0]\ padding[0,0]\
@ -47,7 +45,6 @@ local meta_header = "formspec_version[8]\
" "
-- box[0,0;"..size.x..","..size.y..";]\ -- box[0,0;"..size.x..","..size.y..";]\
-- Prepended to game menus' formspecs.
local game_header = "formspec_version[8]\ local game_header = "formspec_version[8]\
size["..size.x..","..size.y.."]\ size["..size.x..","..size.y.."]\
padding[0,0]\ padding[0,0]\
@ -65,16 +62,20 @@ local content_header = "formspec_version[8]\
bgcolor[#0000;true;#151618]\ bgcolor[#0000;true;#151618]\
" "
-- The default main menu for games.
local default_game_menu = [[ local default_game_menu = [[
<meta> <meta>
enable_clouds = true enable_clouds = true
</meta> </meta>
<main> <main>
@set:test:Hello @if:@selected_world:fi2
label[2,2;Test] @set:list_width:@WIDTH * 0.3
scroll_container[1,6;4,2;worldscroll;vertical;;0,0] @else:fi2
@foreach:$WORLDS:worlds @set:list_width:@WIDTH * 0.8
@endif:fi2
image[${@WIDTH * 0.1 +-0.1},${@HEIGHT * 0.1 +-0.1};${@WIDTH * 0.8 + 0.2},${@HEIGHT * 0.8 + 0.2};$DEFAULT_ASSET_PATH/bg_translucent.png;8,8]
scroll_container[${@WIDTH * 0.1},${@HEIGHT * 0.1};${@list_width},${@HEIGHT * 0.8};worldscroll;vertical;;0,0]
@foreach:@WORLDS:worlds
@if:@i % 2:sel @if:@i % 2:sel
style[.select_world_${@name};bgimg=$DEFAULT_ASSET_PATH/white.png;bgimg_middle=0;bgcolor=#373530] style[.select_world_${@name};bgimg=$DEFAULT_ASSET_PATH/white.png;bgimg_middle=0;bgcolor=#373530]
style[.select_world_${@name}:hovered;bgimg=$DEFAULT_ASSET_PATH/white.png;bgimg_middle=0;bgcolor=#373530] style[.select_world_${@name}:hovered;bgimg=$DEFAULT_ASSET_PATH/white.png;bgimg_middle=0;bgcolor=#373530]
@ -84,9 +85,21 @@ local default_game_menu = [[
style[.select_world_${@name}:hovered;bgimg=$DEFAULT_ASSET_PATH/white.png;bgimg_middle=0;bgcolor=#403e39] style[.select_world_${@name}:hovered;bgimg=$DEFAULT_ASSET_PATH/white.png;bgimg_middle=0;bgcolor=#403e39]
style[.select_world_${@name}:hovered+focused;bgimg=$DEFAULT_ASSET_PATH/white.png;bgimg_middle=0;bgcolor=#403e39] style[.select_world_${@name}:hovered+focused;bgimg=$DEFAULT_ASSET_PATH/white.png;bgimg_middle=0;bgcolor=#403e39]
@endif:sel @endif:sel
button[0,${@i * 0.5};4,0.5;.select_world_${@name};${@test}] button[0,${(@i +-1) * 0.5};${@list_width},0.5;.select_world_${@name};${@name}]
@endforeach:worlds @endforeach:worlds
scroll_container_end[] scroll_container_end[]
@if:@selected_world:fi
box[${@WIDTH * 0.4 +-0.05},${@HEIGHT * 0.1};0.1,${@HEIGHT * 0.8};#292d2fff]
scroll_container[${@WIDTH * 0.4},${@HEIGHT * 0.1};${@WIDTH * 0.8},${@HEIGHT * 0.8};worldmodsscroll;vertical;;0,0]
@foreach:@WORLDMODS:wm
label[0,${@i};${@name}]
@endforeach:wm
scroll_container_end[]
scrollbar[-800,6;0,2;vertical;worldmodsscroll;]
@else:fi
@endif:fi
scrollbaroptions[arrows=hide] scrollbaroptions[arrows=hide]
scrollbar[-800,6;0,2;vertical;worldscroll;] scrollbar[-800,6;0,2;vertical;worldscroll;]
</main> </main>
@ -117,11 +130,6 @@ end
minetest.async_event_handler = handle_job minetest.async_event_handler = handle_job
function minetest.handle_async(func, parameter, callback) function minetest.handle_async(func, parameter, callback)
-- Serialize function
local serialized_func = string.dump(func)
assert(serialized_func ~= nil)
-- Serialize parameters -- Serialize parameters
local serialized_param = minetest.serialize(parameter) local serialized_param = minetest.serialize(parameter)
@ -129,7 +137,7 @@ function minetest.handle_async(func, parameter, callback)
return false return false
end end
local jobid = minetest.do_async_callback(serialized_func, serialized_param) local jobid = minetest.do_async_callback(func, serialized_param)
minetest.async_jobs[jobid] = callback minetest.async_jobs[jobid] = callback
@ -173,13 +181,44 @@ local function get_worlds_for_game(id)
local out = {} local out = {}
for _, x in ipairs(minetest.get_worlds()) do for _, x in ipairs(minetest.get_worlds()) do
if x.gameid == id then if x.gameid == id then
if math.random() > 0.5 then x.selected = true end
out[#out +1] = x out[#out +1] = x
end end
end end
return out return out
end end
function get_mods_for_game(id)
local out = {}
for _, x in ipairs(get_all_mods()) do
local conf = Settings(x.path.."mod.conf")
local unsupported_games = conf:get("unsupported_games")
if unsupported_games then
if table.indexof(unsupported_games:trim():split(","), id) == -1 then
out[#out +1] = x
end
else
local supported_games = conf:get("supported_games")
if supported_games then
if table.indexof(supported_games:trim():split(","), id) ~= -1 then
out[#out +1] = x
end
else
out[#out +1] = x
end
end
end
return out
end
function get_mods_for_world(world)
local config = minetest.check_mod_configuration(world)
for _, x in ipairs(config.unsatisfied_mods) do
x.unsatisfied = true
config.satisfied_mods[#config.satisfied_mods +1] = x
end
return config.satisfied_mods
end
-- Returns a list of content available for download. -- Returns a list of content available for download.
function get_available_content() function get_available_content()
local version = minetest.get_version() local version = minetest.get_version()
@ -357,6 +396,10 @@ will define a dialog named 'main', with a label in it, and a dialog named 'other
with a button in it. Every menu file must have a dialog named 'main', which serves with a button in it. Every menu file must have a dialog named 'main', which serves
as the entry point of the game's menu. as the entry point of the game's menu.
Note that to prevent dialog definitions from conflicting with the contents of
hypertext[] elements, they must not have leading whitespace before the opening
delimiter.
Being able to define these other dialogs would be pretty pointless if they couldn't Being able to define these other dialogs would be pretty pointless if they couldn't
be used for anything. Accordingly, you can use standard formspec actions, e.g. buttons, be used for anything. Accordingly, you can use standard formspec actions, e.g. buttons,
to segue to a different dialog. To do this, set the action name to to segue to a different dialog. To do this, set the action name to
@ -393,9 +436,13 @@ file's top-level object will correspond to a variable. (Note that variable names
only contain letters or numbers, even if mapped from a JSON file.) only contain letters or numbers, even if mapped from a JSON file.)
The menu-provided lists are: The menu-provided lists are:
- $WORLDS: The list of worlds for this game. Exposes @name (the world name), - @WORLDS: The list of worlds for this game. Exposes @name (the world name),
@path (the world's full absolute path), @gameid (the ID of the current game), @path (the world's full absolute path), @gameid (the ID of the current game),
and @selected (whether the world is currently selected). and @selected (whether the world is currently selected).
- @MODS: The list of installed mods that are not incompatible with this game.
Exposes @name, @title, @description, @author, @path, @depends, and @optional_depends.
- @WORLDMODS: The list of mods installed on the current world. No-op if no world
is selected.
Expressions also support rudimentary mathematical operations, namely addition (+), Expressions also support rudimentary mathematical operations, namely addition (+),
subtraction (+-), multiplication (*), division (/), and exponentiation (^). Trying subtraction (+-), multiplication (*), division (/), and exponentiation (^). Trying
@ -436,6 +483,12 @@ Example:
@endif:<name> @endif:<name>
``` ```
Notes:
- The only uniqueness requirements for the name of a block is that it not be
the name of a statment of the same type contained in the body of that block.
This is so that the parser knows which `end` belongs to which block without
having to manage state.
Note: Because of the way the main menu works, image paths must be specified in full. Note: Because of the way the main menu works, image paths must be specified in full.
To make this non-painful, when referencing images use '$ASSET_PATH/<image name>' To make this non-painful, when referencing images use '$ASSET_PATH/<image name>'
instead of just the image name. $ASSET_PATH will be replaced with the actual path instead of just the image name. $ASSET_PATH will be replaced with the actual path
@ -444,7 +497,7 @@ there. You can use $ASSET_PATH in any context. Additionally, $DEFAULT_ASSET_PATH
the path to the builtin assets folder, and $NO_IMAGE is the path to blank.png, in the path to the builtin assets folder, and $NO_IMAGE is the path to blank.png, in
case you need to stylistically unset a background image defined by a global style. case you need to stylistically unset a background image defined by a global style.
--]] --]]
local function build_template_dialog(fs) local function build_template_dialog(fs, depth)
if fs:trim() == "" then return end if fs:trim() == "" then return end
local dialog = {} local dialog = {}
local i = 0 local i = 0
@ -452,41 +505,42 @@ local function build_template_dialog(fs)
-- Extract foreach loops -- Extract foreach loops
local prev = 1 local prev = 1
while i < 1000 do while i < 1000 do
local unfound = 0
local a, b, pattern, name, content = fs:find("@foreach:([^:]+):(%w*)\n(.-)\n%s-@endforeach:%2", prev) local a, b, pattern, name, content = fs:find("@foreach:([^:]+):(%w*)\n(.-)\n%s-@endforeach:%2", prev)
if not a then break end -- print(string.rep("-", 20)..(depth or 0))
if a then
-- print("for each "..pattern.." ("..name..")\n"..content.."\nend for each ("..name..")") -- print("for each "..pattern.." ("..name..")\n"..content.."\nend for each ("..name..")")
dialog[#dialog +1] = build_template_dialog(fs:sub(prev +1, a -1)) dialog[#dialog +1] = build_template_dialog(fs:sub(prev, a -1), (depth or 0) +1)
dialog[#dialog +1] = { dialog[#dialog +1] = {
foreach = pattern:trim(), foreach = pattern:trim(),
name = name, name = name,
content = build_template_dialog(content:trim()) content = build_template_dialog(content:trim(), (depth or 0) +1)
} }
prev = b prev = b
i = i +1 i = i +1
end
if i > 0 then
dialog[#dialog +1] = build_template_dialog(fs:sub(prev +1))
else else
-- Extract conditionals unfound = unfound +1
prev = 0 end
while i < 1000 do
local a, b, expr, name, content, else_content = fs:find("@if:([^:]+):(%w*)\n(.-)\n%s-@else:%2\n(.-\n?)@endif:%2", prev) local a, b, expr, name, content, else_content = fs:find("@if:([^:]+):(%w*)\n(.-)\n%s-@else:%2\n(.-\n?)@endif:%2", prev)
if not a then break end if a then
-- print("for each "..pattern.." ("..name..")\n"..content.."\nend for each ("..name..")") -- print("if "..expr.." ("..name..")\n"..content.."\nend if ("..name..")")
dialog[#dialog +1] = fs:sub(prev +1, a -1) dialog[#dialog +1] = build_template_dialog(fs:sub(prev +1, a -1), (depth or 0) +1)
dialog[#dialog +1] = { dialog[#dialog +1] = {
condition = expr:trim(), condition = expr:trim(),
name = name, name = name,
content = build_template_dialog(content:trim()), content = build_template_dialog(content:trim(), (depth or 0) +1),
else_content = build_template_dialog(else_content:trim()) else_content = build_template_dialog(else_content:trim(), (depth or 0) +1)
} }
prev = b prev = b
i = i +1 i = i +1
else
unfound = unfound +1
end end
if i > 0 then if unfound > 1 then break end
dialog[#dialog +1] = fs:sub(prev +1)
end end
if prev > 1 then
dialog[#dialog +1] = build_template_dialog(fs:sub(prev +1))
end end
if #dialog == 1 then dialog = dialog[1] end if #dialog == 1 then dialog = dialog[1] end
@ -497,19 +551,20 @@ end
local function build_game_menu(input) local function build_game_menu(input)
-- MARK: Environment variables -- MARK: Environment variables
input = input input = "\n"..input
:gsub("%$ASSET_PATH", fe(state.current_game.path.."/menu")) :gsub("%$ASSET_PATH", fe(state.current_game.path.."/menu"))
:gsub("%$DEFAULT_ASSET_PATH", fe(assets:sub(1, #assets -1))) :gsub("%$DEFAULT_ASSET_PATH", fe(assets:sub(1, #assets -1)))
:gsub("%$NO_IMAGE", fe(default_textures.."blank.png")) :gsub("%$NO_IMAGE", fe(default_textures.."blank.png"))
:gsub("/%*.-%*/", "") :gsub("/%*.-%*/", "")
local menu = {} local menu = {}
for name, fs in input:gmatch("<(%l+)>(.-)</%1>") do for name, fs in input:gmatch("%f[^\n]<(%l+)>(.-)%f[^\n]</%1>") do
if name == "meta" then if name == "meta" then
menu[name] = parse_conf_text(fs) menu[name] = parse_conf_text(fs)
else else
menu[name] = build_template_dialog(fs) menu[name] = build_template_dialog(fs)
end end
end end
-- print(dump(menu, " "))
return menu return menu
end end
@ -629,13 +684,25 @@ local function evaluate_template_expression(expr, vars, depth)
-- Expand all variables so we can deal with a constexpr. -- Expand all variables so we can deal with a constexpr.
local offset = 1 local offset = 1
while offset < 100000 do while offset < 100000 do
local a, b, name = expr:find("@([%a]+)", offset) local a, b, name = expr:find("@([%a_]+)", offset)
if not a then break end if not a then break end
-- If referencing an undefined variable, default to 0 because it's safest that way. -- If referencing an undefined variable, default to 0 because it's safest that way.
local result = minetest.formspec_escape(tostring(vars[name] or "0")) local result = minetest.formspec_escape(tostring(vars[name] or "0"))
expr = expr:sub(1, a -1)..result..expr:sub(b +1) expr = expr:sub(1, a -1)..result..expr:sub(b +1)
offset = a +#result offset = a +#result
end end
-- Condense sub-expressions.
local offset = 1
while offset < 100000 do
local a, b, se = expr:find("(%b())", offset)
if not a then break end
se = se:gsub("^%((.*)%)$", "%1")
local result = evaluate_template_expression(se, vars, depth +1)
expr = expr:sub(1, a -1)..result..expr:sub(b +1)
offset = a +#result
end
-- If there are no operators, this is a constant expression and we can just return. -- If there are no operators, this is a constant expression and we can just return.
if not expr:find("%p") then return expr end if not expr:find("%p") then return expr end
@ -649,7 +716,7 @@ local function evaluate_template_block(fs, vars)
-- Assignment statements -- Assignment statements
local offset = 1 local offset = 1
while offset < #fs do while offset < #fs do
local a, b, name, expr = fs:find("@set:(%w+):(.-)\n", offset) local a, b, name, expr = fs:find("@set:([%a_]+):([^\n]+)", offset)
if not a then break end if not a then break end
vars[name] = evaluate_template_expression(expr, vars) vars[name] = evaluate_template_expression(expr, vars)
fs = fs:sub(1, a -1)..fs:sub(b +1) fs = fs:sub(1, a -1)..fs:sub(b +1)
@ -673,8 +740,17 @@ local function evaluate_template_foreach(loop, vars)
local list = {} local list = {}
if loop.foreach:sub(1,1) == "[" then if loop.foreach:sub(1,1) == "[" then
list = loop.foreach:gsub("^%[(.*)%]$", "%1"):split(",") list = loop.foreach:gsub("^%[(.*)%]$", "%1"):split(",")
elseif loop.foreach == "$WORLDS" then elseif loop.foreach:sub(1, 1) == "@" then
local var = loop.foreach:sub(2)
if var == "WORLDS" then
list = get_worlds_for_game(state.current_game.id) list = get_worlds_for_game(state.current_game.id)
elseif var == "MODS" then
list = get_mods_for_game(state.current_game.id)
elseif var == "WORLDMODS" then
list = state.current_world and get_mods_for_world(state.current_world) or {}
else
list = vars[var]
end
end end
for i, x in ipairs(list) do for i, x in ipairs(list) do
local vars2 = {} local vars2 = {}
@ -704,23 +780,21 @@ local function evaluate_template_conditional(cond, vars)
return out return out
end end
-- Game dialogs might contain e.g. foreach loops, so build those if needed. -- Process the syntax tree for a game dialog and output the resulting string.
function evaluate_game_dialog(dialog, vars) function evaluate_game_dialog(dialog, vars)
local out = "" local out = ""
if type(dialog) == "string" then return evaluate_template_block(dialog, vars) end if not dialog then return out end
for _, x in ipairs(dialog) do if type(dialog) == "string" then
if type(x) == "string" then return evaluate_template_block(dialog, vars)
out = out..evaluate_template_block(x, vars) elseif dialog.condition then
elseif x.condition then out = out..evaluate_template_conditional(dialog, vars)
out = out..evaluate_template_conditional(x, vars) elseif dialog.foreach then
elseif x.foreach then out = out..evaluate_template_foreach(dialog, vars)
out = out..evaluate_template_foreach(x, vars)
else else
for _, c in ipairs(x) do for _, c in ipairs(dialog) do
out = out..evaluate_game_dialog(c, vars) out = out..evaluate_game_dialog(c, vars)
end end
end end
end
return out return out
end end
@ -763,12 +837,9 @@ function show_meta_menu(v)
local dist = i -idx local dist = i -idx
local scale = 4 /(math.abs(dist) +1) local scale = 4 /(math.abs(dist) +1)
if scale > 0.1 then if scale > 0.1 then
--This looks cool, but is useless for UI purposes: center +10^(1 /math.abs(dist)) *math.sign(dist) --This looks neat, but is useless for UI purposes: center +10^(1 /math.abs(dist)) *math.sign(dist)
local lc = center +math.abs(dist *10)^0.55 *math.sign(dist) local lc = center +math.abs(dist *10)^0.55 *math.sign(dist)
local test = io.open(x.menuicon_path) if x.menuicon_path == "" then
if test then
test:close()
else
x.menuicon_path = assets.."games.png" x.menuicon_path = assets.."games.png"
end end
--TODO: Do these need some kind of background? --TODO: Do these need some kind of background?
@ -898,6 +969,7 @@ function show_game_menu(args)
for i, x in ipairs(state.menu_current) do for i, x in ipairs(state.menu_current) do
fs = fs..evaluate_game_dialog(game.menu[x], setmetatable({WIDTH = size.x, HEIGHT = size.y}, {__index = state.menu_vars})) fs = fs..evaluate_game_dialog(game.menu[x], setmetatable({WIDTH = size.x, HEIGHT = size.y}, {__index = state.menu_vars}))
end end
-- print(fs)
minetest.update_formspec(game_header..fs.."\ minetest.update_formspec(game_header..fs.."\
button[0,"..(size.y -1)..";1,1;to_meta_menu;<]\ button[0,"..(size.y -1)..";1,1;to_meta_menu;<]\
@ -912,6 +984,23 @@ function add_favorite_server(address, port)
f:close() f:close()
end end
function remove_favorite_server(address, port)
local idx
for i, x in pairs(state.favorite_servers) do
if x.address == address and x.port == port then
idx = i
break
end
end
if idx then
table.remove(state.favorite_servers, idx)
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
end
function refresh_server_list() function refresh_server_list()
minetest.handle_async( minetest.handle_async(
function(param) function(param)
@ -934,6 +1023,9 @@ function refresh_server_list()
local list = table.copy(state.favorite_servers) local list = table.copy(state.favorite_servers)
table.insert_all(list, result) table.insert_all(list, result)
state.serverlist = list state.serverlist = list
if state.serverlist_filtered then
state.serverlist_filtered = search_server_list(state.servers_filter)
end
if state.loc == "servers" then if state.loc == "servers" then
show_servers_menu() show_servers_menu()
end end
@ -1363,15 +1455,18 @@ function show_content_menu()
fs = fs.."\ fs = fs.."\
box[0,0;"..size.x..",1;#403e39ff]\ box[0,0;"..size.x..",1;#403e39ff]\
box[0,1;"..size.x..",0.1;#292d2fff]\ box[0,1;"..size.x..",0.1;#292d2fff]\
style[content_search,content_cancel;bgimg="..assets.."btn_bg_2.png;bgimg_middle=8,8]\ style[content_search,content_cancel;bgimg="..assets.."btn_bg_2_dark.png;bgimg_middle=8,8]\
image_button[0.125,0.125;0.75,0.75;"..assets.."search.png;content_search;]\ image_button[0.125,0.125;0.75,0.75;"..assets.."search.png;content_search;]\
tooltip[content_search;Search;#444;#aaa]\
image_button[1,0.125;0.75,0.75;"..assets.."cancel.png;content_cancel;]\ image_button[1,0.125;0.75,0.75;"..assets.."cancel.png;content_cancel;]\
tooltip[content_cancel;Cancel;#444;#aaa]\
style[test;bgimg="..assets.."white.png;bgimg_middle=0;bgcolor=#403e39ff]\ style[test;bgimg="..assets.."white.png;bgimg_middle=0;bgcolor=#403e39ff]\
style[test:hovered;bgimg="..assets.."white.png;bgimg_middle=0]\ 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]\ 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;;]\ 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 -6)..",0;4,1;test;Browse Online Content...]\
button["..(w -3)..",0;3,1;test;Add Content...]\ image_button["..(w -1.5)..",0.125;0.75,0.75;"..assets.."new_package.png;new_package;]\
tooltip[new_package;New Package...;#444;#aaa]\
" "
local start = w /2 -(pages *0.125) local start = w /2 -(pages *0.125)
@ -1392,7 +1487,8 @@ function show_content_menu()
end end
function minetest.button_handler(data) function minetest.button_handler(data)
state.menu_vars = data if not state.menu_vars then state.menu_vars = {} end
setmetatable(state.menu_vars, {__index = data})
if state.loc == "games" then if state.loc == "games" then
if data.content or data.ht == "action:content" then if data.content or data.ht == "action:content" then
if state.loc ~= "content" then show_content() end if state.loc ~= "content" then show_content() end
@ -1422,11 +1518,16 @@ function minetest.button_handler(data)
if data.servers_refresh then if data.servers_refresh then
refresh_server_list() refresh_server_list()
elseif data.servers_search or data.key_enter_field == "servers_filter" then 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) state.serverlist_filtered = search_server_list(data.servers_filter)
show_servers_menu() show_servers_menu()
elseif data.servers_cancel then elseif data.servers_cancel then
state.serverlist_filtered = nil state.serverlist_filtered = nil
show_servers_menu() show_servers_menu()
elseif data.unfavorite_server then
state.current_server.favorite = nil
remove_favorite_server(state.current_server.address, state.current_server.port)
show_servers_menu()
elseif data.show_server_mods then elseif data.show_server_mods then
state.showing_server_mods = true state.showing_server_mods = true
show_servers_menu() show_servers_menu()
@ -1449,6 +1550,9 @@ function minetest.button_handler(data)
show_servers_menu() show_servers_menu()
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 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("name", data.server_username)
local address
local port
if state.connecting_to_server then
if not tonumber(data.server_port) then if not tonumber(data.server_port) then
state.server_connection_error = { state.server_connection_error = {
msg = "Invalid port.", msg = "Invalid port.",
@ -1458,14 +1562,11 @@ function minetest.button_handler(data)
state.server_connection_error = nil state.server_connection_error = nil
return return
end end
local address
local port
if state.connecting_to_server then
address = data.server_address:lower() address = data.server_address:lower()
port = data.server_port:lower() port = data.server_port:lower()
else else
address = state.current_server.address:lower() address = state.current_server.address:lower()
port = state.current_server.port:lower() port = state.current_server.port
end end
local is_favorite = false local is_favorite = false
for _, x in ipairs(state.favorite_servers) do for _, x in ipairs(state.favorite_servers) do
@ -1523,10 +1624,17 @@ function minetest.button_handler(data)
show_game_menu { show_game_menu {
overlay_dialog = k:sub(string.len(".overlay_dialog_>")) overlay_dialog = k:sub(string.len(".overlay_dialog_>"))
} }
elseif k:sub(1, string.len(".select_world_")) == ".select_world_" then
state.menu_vars.selected_world = k:sub(string.len(".select_world_>"))
show_game_menu()
elseif k == ".unoverlay_dialog" then elseif k == ".unoverlay_dialog" then
show_game_menu { show_game_menu {
unoverlay_dialog = true unoverlay_dialog = true
} }
elseif k:sub(1, string.len(".set_")) == ".set_" then
local name, value = k:match "%.set_(.-)_to_(.*)"
state.menu_vars[name] = value == "" and "0" or value
show_game_menu()
end end
end end
end end